Merge pull request #118 from 0pen-dash/revert-117-avereha/merge-dev-2

Revert "avereha/merge dev 2"
This commit is contained in:
Andrei Vereha 2021-09-15 00:13:06 +02:00 committed by GitHub
commit b8eefa842d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 134 additions and 224 deletions

View file

@ -1,7 +1,42 @@
package info.nightscout.androidaps
import android.os.SystemClock
import android.view.View
import android.view.ViewGroup
import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.scrollTo
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withClassName
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withTagValue
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
import androidx.test.rule.GrantPermissionRule
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.source.RandomBgPlugin
import info.nightscout.androidaps.setupwizard.SetupWizardActivity
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.extensions.isRunningTest
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.hamcrest.TypeSafeMatcher
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@LargeTest

View file

@ -8,18 +8,14 @@ import android.widget.ArrayAdapter
import com.google.common.base.Joiner
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.DialogProfileswitchBinding
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -35,9 +31,6 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var config: Config
@Inject lateinit var hardLimits: HardLimits
@Inject lateinit var rxBus: RxBusWrapper
private var profileIndex: Int? = null
@ -136,33 +129,22 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
activity?.let { activity ->
val ps = profileFunction.buildProfileSwitch(profileStore, profileName, duration, percent, timeShift, eventTime)
val validity = ProfileSealed.PS(ps).isValid(resourceHelper.gs(R.string.careportal_profileswitch), activePlugin.activePump, config, resourceHelper, rxBus, hardLimits)
if (validity.isValid)
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
profileFunction.createProfileSwitch(profileStore,
profileName = profileName,
durationInMinutes = duration,
percentage = percent,
timeShiftInHours = timeShift,
timestamp = eventTime)
uel.log(Action.PROFILE_SWITCH,
Sources.ProfileSwitchDialog,
notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.SimpleString(profileName),
ValueWithUnit.Percent(percent),
ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 },
ValueWithUnit.Minute(duration).takeIf { duration != 0 })
})
else {
OKDialog.show(
activity,
resourceHelper.gs(R.string.careportal_profileswitch),
HtmlHelper.fromHtml(Joiner.on("<br/>").join(validity.reasons))
)
return false
}
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
profileFunction.createProfileSwitch(profileStore,
profileName = profileName,
durationInMinutes = duration,
percentage = percent,
timeShiftInHours = timeShift,
timestamp = eventTime)
uel.log(Action.PROFILE_SWITCH,
Sources.ProfileSwitchDialog,
notes,
ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged },
ValueWithUnit.SimpleString(profileName),
ValueWithUnit.Percent(percent),
ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 },
ValueWithUnit.Minute(duration).takeIf { duration != 0 })
})
}
return true
}

View file

@ -46,6 +46,7 @@ import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAc
import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.receivers.ReceiverStatusStore
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy

View file

@ -125,11 +125,11 @@ class OpenAPSAMAPlugin @Inject constructor(
maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
}
if (!hardLimits.checkHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
if (!hardLimits.checkHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
startPart = System.currentTimeMillis()
if (constraintChecker.isAutosensModeEnabled().value()) {
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin")

View file

@ -130,11 +130,11 @@ class OpenAPSSMBPlugin @Inject constructor(
maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
}
if (!hardLimits.checkHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
if (!hardLimits.checkHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return
if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return
startPart = System.currentTimeMillis()
if (constraintChecker.isAutosensModeEnabled().value()) {
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin")

View file

@ -91,10 +91,10 @@ class ProfileFunctionImplementation @Inject constructor(
if (sp.getString(R.string.key_units, Constants.MGDL) == Constants.MGDL) GlucoseUnit.MGDL
else GlucoseUnit.MMOL
override fun buildProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) : ProfileSwitch {
override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) {
val pureProfile = profileStore.getSpecificProfile(profileName)
?: throw InvalidParameterSpecException(profileName)
return ProfileSwitch(
val ps = ProfileSwitch(
timestamp = timestamp,
basalBlocks = pureProfile.basalBlocks,
isfBlocks = pureProfile.isfBlocks,
@ -105,14 +105,8 @@ class ProfileFunctionImplementation @Inject constructor(
timeshift = T.hours(timeShiftInHours.toLong()).msecs(),
percentage = percentage,
duration = T.mins(durationInMinutes.toLong()).msecs(),
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
it.insulinEndTime = (pureProfile.dia * 3600 * 1000).toLong()
}
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also { it.insulinEndTime = (pureProfile.dia * 3600 * 1000).toLong() }
)
}
override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) {
val ps = buildProfileSwitch(profileStore, profileName, durationInMinutes, percentage, timeShiftInHours, timestamp)
disposable += repository.runTransactionForResult(InsertOrUpdateProfileSwitch(ps))
.subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") }

View file

@ -740,7 +740,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
graphData.addTreatments()
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
graphData.addActivity(0.8)
if ((pump.pumpDescription.isTempBasalCapable || config.NSCLIENT) && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
graphData.addBasals()
graphData.addTargetLine()
graphData.addNowLine(dateUtil.now())

View file

@ -28,7 +28,6 @@ import info.nightscout.androidaps.setupwizard.elements.*
import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
import info.nightscout.androidaps.utils.AndroidPermission
import info.nightscout.androidaps.utils.CryptoUtil
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.extensions.isRunningTest
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -54,8 +53,7 @@ class SWDefinition @Inject constructor(
private val importExportPrefs: ImportExportPrefs,
private val androidPermission: AndroidPermission,
private val cryptoUtil: CryptoUtil,
private val config: Config,
private val hardLimits: HardLimits
private val config: Config
) {
lateinit var activity: AppCompatActivity
@ -257,7 +255,7 @@ class SWDefinition @Inject constructor(
.add(SWFragment(injector, this)
.add(LocalProfileFragment()))
.validator {
localProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, resourceHelper, rxBus, hardLimits).isValid }
localProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, resourceHelper, rxBus) }
?: false
}
.visibility { localProfilePlugin.isEnabled() }

View file

@ -1,8 +1,7 @@
package info.nightscout.androidaps.utils
import android.content.Context
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction
import info.nightscout.androidaps.logging.AAPSLogger
@ -16,7 +15,6 @@ import javax.inject.Singleton
import kotlin.math.max
import kotlin.math.min
@OpenForTesting
@Singleton
class HardLimits @Inject constructor(
private val aapsLogger: AAPSLogger,
@ -83,12 +81,9 @@ class HardLimits @Inject constructor(
fun maxIC(): Double = MAX_IC[loadAge()]
// safety checks
fun checkHardLimits(value: Double, valueName: Int, lowLimit: Double, highLimit: Double): Boolean =
fun checkOnlyHardLimits(value: Double, valueName: Int, lowLimit: Double, highLimit: Double): Boolean =
value == verifyHardLimits(value, valueName, lowLimit, highLimit)
fun isInRange(value: Double, lowLimit: Double, highLimit: Double): Boolean =
value in lowLimit..highLimit
fun verifyHardLimits(value: Double, valueName: Int, lowLimit: Double, highLimit: Double): Double {
var newValue = value
if (newValue < lowLimit || newValue > highLimit) {
@ -103,4 +98,4 @@ class HardLimits @Inject constructor(
}
return newValue
}
}
}

View file

@ -250,6 +250,8 @@
<string name="smscommunicator_loophasbeendisabled">Loop has been disabled</string>
<string name="smscommunicator_loophasbeenenabled">Loop has been enabled</string>
<string name="smscommunicator_loopisenabled">Loop is enabled</string>
<string name="valuelimitedto">%1$.2f limited to %2$.2f</string>
<string name="valueoutofrange">»%1$s« is out of hard limits</string>
<string name="smscommunicator_pumpconnectwithcode">To connect pump reply with code %1$s</string>
<string name="smscommunicator_pumpconnectfail">Connection to pump failed</string>
<string name="smscommunicator_pumpdisconnectwithcode">To disconnect pump for %1$d minutes reply with code %2$s</string>
@ -398,6 +400,12 @@
<string name="adult">Adult</string>
<string name="resistantadult">Insulin resistant adult</string>
<string name="pregnant">Pregnancy</string>
<string name="key_age" translatable="false">age</string>
<string name="key_child" translatable="false">child</string>
<string name="key_teenage" translatable="false">teenage</string>
<string name="key_adult" translatable="false">adult</string>
<string name="key_resistantadult" translatable="false">resistantadult</string>
<string name="key_pregnant" translatable="false">pregnant</string>
<string name="patientage_summary">Please select patient type to setup safety limits</string>
<string name="patient_name">Patient name</string>
<string name="patient_name_summary">Please provide patient name or nickname to differentiate among multiple setups</string>
@ -1074,6 +1082,16 @@
<string name="email_address">Email address</string>
<string name="privacy_settings">Privacy setting</string>
<string name="privacy_summary">You can provide optional email address if you want to be notified about app crashes. This is not an automated service. You will be contacted by developers in dangerous situations.</string>
<string name="profile_low_target">Profile low target</string>
<string name="profile_high_target">Profile high target</string>
<string name="temp_target_low_target">Temporary target bottom value</string>
<string name="temp_target_high_target">Temporary target top value</string>
<string name="temp_target_value">Temporary target value</string>
<string name="profile_dia">Profile DIA value</string>
<string name="profile_sensitivity_value">Profile sensitivity value</string>
<string name="profile_max_daily_basal_value">Maximal profile basal value</string>
<string name="current_basal_value">Current basal value</string>
<string name="profile_carbs_ratio_value">Profile carbs ratio value</string>
<string name="full_sync">Full sync</string>
<string name="prime">Prime</string>
<string name="ns_sync_options">Synchronization</string>

View file

@ -19,8 +19,6 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONArray
@ -96,93 +94,34 @@ sealed class ProfileSealed(
value.timeZone.rawOffset.toLong()
)
override fun isValid(from: String, pump: Pump, config: Config, resourceHelper: ResourceHelper, rxBus: RxBusWrapper, hardLimits: HardLimits): Profile.ValidityCheck {
override fun isValid(from: String, pump: Pump, config: Config, resourceHelper: ResourceHelper, rxBus: RxBusWrapper): Boolean {
val notify = true
val validityCheck = Profile.ValidityCheck()
var valid = true
val description = pump.pumpDescription
for (basal in basalBlocks) {
val basalAmount = basal.amount * percentage / 100.0
if (!description.is30minBasalRatesCapable) {
if (!description.is30minBasalRatesCapable) {
for (basal in basalBlocks) {
// Check for hours alignment
val duration: Long = basal.duration
if (duration % 3600000 != 0L) {
if (notify && config.APS) {
val notification = Notification(
Notification.BASAL_PROFILE_NOT_ALIGNED_TO_HOURS,
resourceHelper.gs(R.string.basalprofilenotaligned, from),
Notification.NORMAL
)
val notification = Notification(Notification.BASAL_PROFILE_NOT_ALIGNED_TO_HOURS, resourceHelper.gs(R.string.basalprofilenotaligned, from), Notification.NORMAL)
rxBus.send(EventNewNotification(notification))
}
validityCheck.isValid = false
validityCheck.reasons.add(
resourceHelper.gs(
R.string.basalprofilenotaligned,
from
)
)
break
valid = false
}
// Check for minimal basal value
if (basal.amount < description.basalMinimumRate) {
basal.amount = description.basalMinimumRate
if (notify) sendBelowMinimumNotification(from, rxBus, resourceHelper)
valid = false
} else if (basal.amount > description.basalMaximumRate) {
basal.amount = description.basalMaximumRate
if (notify) sendAboveMaximumNotification(from, rxBus, resourceHelper)
valid = false
}
}
// Check for minimal basal value
if (basalAmount < description.basalMinimumRate) {
basal.amount = description.basalMinimumRate
if (notify) sendBelowMinimumNotification(from, rxBus, resourceHelper)
validityCheck.isValid = false
validityCheck.reasons.add(resourceHelper.gs(R.string.minimalbasalvaluereplaced, from))
break
} else if (basalAmount > description.basalMaximumRate) {
basal.amount = description.basalMaximumRate
if (notify) sendAboveMaximumNotification(from, rxBus, resourceHelper)
validityCheck.isValid = false
validityCheck.reasons.add(resourceHelper.gs(R.string.maximumbasalvaluereplaced, from))
break
}
if (!hardLimits.isInRange(basalAmount, 0.01, hardLimits.maxBasal())) {
validityCheck.isValid = false
validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.basal_value), basalAmount))
break
}
}
if (!hardLimits.isInRange(dia, hardLimits.minDia(), hardLimits.maxDia())) {
validityCheck.isValid = false
validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_dia), dia))
}
for (ic in icBlocks)
if (!hardLimits.isInRange(ic.amount * 100.0 / percentage, hardLimits.minIC(), hardLimits.maxIC())) {
validityCheck.isValid = false
validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_carbs_ratio_value), ic.amount * 100.0 / percentage))
break
}
for (isf in isfBlocks)
if (!hardLimits.isInRange(toMgdl(isf.amount * 100.0 / percentage, units), HardLimits.MIN_ISF, HardLimits.MAX_ISF)) {
validityCheck.isValid = false
validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_sensitivity_value), isf.amount * 100.0 / percentage))
break
}
for (target in targetBlocks) {
if (hardLimits.isInRange(
Round.roundTo(target.lowTarget, 0.1),
HardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(),
HardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble()
)
) {
validityCheck.isValid = false
validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_low_target), target.lowTarget))
break
}
if (hardLimits.isInRange(
Round.roundTo(target.highTarget, 0.1),
HardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(),
HardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble()
)
) {
validityCheck.isValid = false
validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_high_target), target.highTarget))
break
}
}
return validityCheck
return valid
}
protected open fun sendBelowMinimumNotification(from: String, rxBus: RxBusWrapper, resourceHelper: ResourceHelper) {

View file

@ -17,18 +17,15 @@ import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.extensions.getCustomizedName
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONObject
import java.io.File.separator
import java.text.DecimalFormat
import javax.inject.Inject
@ -42,7 +39,6 @@ class ProfileViewerDialog : DaggerDialogFragment() {
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var config: Config
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var hardLimits: HardLimits
private var time: Long = 0
@ -153,9 +149,7 @@ class ProfileViewerDialog : DaggerDialogFragment() {
}
binding.noprofile.visibility = View.GONE
val validity = profile1.isValid("ProfileViewDialog", activePlugin.activePump, config, resourceHelper, rxBus, hardLimits)
binding.invalidprofile.text = resourceHelper.gs(R.string.invalidprofile) + "\n" + validity.reasons.joinToString(separator = "\n")
binding.invalidprofile.visibility = validity.isValid.not().toVisibility()
binding.invalidprofile.visibility = if (profile1.isValid("ProfileViewDialog", activePlugin.activePump, config, resourceHelper, rxBus)) View.GONE else View.VISIBLE
}
else
profile?.let {
@ -170,9 +164,7 @@ class ProfileViewerDialog : DaggerDialogFragment() {
binding.basalGraph.show(it)
binding.noprofile.visibility = View.GONE
val validity = it.isValid("ProfileViewDialog", activePlugin.activePump, config, resourceHelper, rxBus, hardLimits)
binding.invalidprofile.text = resourceHelper.gs(R.string.invalidprofile) + "\n" + validity.reasons.joinToString(separator = "\n")
binding.invalidprofile.visibility = validity.isValid.not().toVisibility()
binding.invalidprofile.visibility = if (it.isValid("ProfileViewDialog", activePlugin.activePump, config, resourceHelper, rxBus)) View.GONE else View.VISIBLE
}
}

View file

@ -6,7 +6,6 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter.to0Decimal
import info.nightscout.androidaps.utils.DecimalFormatter.to1Decimal
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.joda.time.DateTime
@ -14,9 +13,7 @@ import org.json.JSONObject
interface Profile {
class ValidityCheck(var isValid: Boolean = true, val reasons: ArrayList<String> = arrayListOf())
fun isValid(from: String, pump: Pump, config: Config, resourceHelper: ResourceHelper, rxBus: RxBusWrapper, hardLimits: HardLimits): ValidityCheck
fun isValid(from: String, pump: Pump, config: Config, resourceHelper: ResourceHelper, rxBus: RxBusWrapper): Boolean
/**
* Units used for ISF & target

View file

@ -46,18 +46,6 @@ interface ProfileFunction {
*/
fun getRequestedProfile(): ProfileSwitch?
/**
* Build a new circadian profile switch request based on provided profile
*
* @param profileStore ProfileStore to use
* @param profileName this profile from profile store
* @param durationInMinutes
* @param percentage 100 = no modification
* @param timeShiftInHours 0 = no modification
* @param timestamp expected time
*/
fun buildProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long): ProfileSwitch
/**
* Create a new circadian profile switch request based on provided profile
*

View file

@ -63,7 +63,7 @@ class PumpDescription() {
is30minBasalRatesCapable = false
isRefillingCapable = true
isBatteryReplaceable = true
storesCarbInfo = false
storesCarbInfo = true
supportsTDDs = false
needsManualTDDLoad = true
hasCustomUnreachableAlertCheck = false

View file

@ -90,11 +90,7 @@ class SPImplementation @Inject constructor(
return try {
sharedPreferences.getLong(key, defaultValue)
} catch (e: Exception) {
try {
SafeParse.stringToLong(sharedPreferences.getString(key, defaultValue.toString()))
} catch (e: Exception) {
defaultValue
}
SafeParse.stringToLong(sharedPreferences.getString(key, defaultValue.toString()))
}
}

View file

@ -57,12 +57,6 @@
<string name="key_active_pump_change_timestamp" translatable="false">active_pump_change_timestamp</string>
<string name="key_active_pump_type" translatable="false">active_pump_type</string>
<string name="key_active_pump_serial_number" translatable="false">active_pump_serial_number</string>
<string name="key_age" translatable="false">age</string>
<string name="key_child" translatable="false">child</string>
<string name="key_teenage" translatable="false">teenage</string>
<string name="key_adult" translatable="false">adult</string>
<string name="key_resistantadult" translatable="false">resistantadult</string>
<string name="key_pregnant" translatable="false">pregnant</string>
<!-- General-->
<string name="refresh">Refresh</string>
@ -478,22 +472,6 @@
<string name="uel_loop_removed">LOOP REMOVED</string>
<string name="uel_other">OTHER</string>
<!-- HardLimits -->
<string name="profile_low_target">Profile low target</string>
<string name="profile_high_target">Profile high target</string>
<string name="temp_target_low_target">Temporary target bottom value</string>
<string name="temp_target_high_target">Temporary target top value</string>
<string name="temp_target_value">Temporary target value</string>
<string name="profile_dia">Profile DIA value</string>
<string name="profile_sensitivity_value">Profile sensitivity value</string>
<string name="profile_max_daily_basal_value">Maximal profile basal value</string>
<string name="current_basal_value">Current basal value</string>
<string name="profile_carbs_ratio_value">Profile carbs ratio value</string>
<string name="valuelimitedto">%1$.2f limited to %2$.2f</string>
<string name="valueoutofrange">»%1$s« is out of hard limits</string>
<string name="value_out_of_hard_limits">»%1$s« %2$.2f is out of hard limits</string>
<string name="basal_value">Basal value</string>
<plurals name="days">
<item quantity="one">%1$d day</item>
<item quantity="other">%1$d days</item>

View file

@ -2,10 +2,11 @@ package info.nightscout.androidaps.data
import android.content.Context
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBase
import info.nightscout.androidaps.TestPumpPlugin
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
@ -13,18 +14,18 @@ import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.TestAapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONObject
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyString
import org.mockito.Mockito.doNothing
import java.util.*
/**
@ -37,29 +38,25 @@ class ProfileTest : TestBase() {
@Mock lateinit var resourceHelper: ResourceHelper
@Mock lateinit var context: Context
@Mock lateinit var config: Config
@Mock lateinit var sp: SP
@Mock lateinit var repository: AppRepository
private lateinit var rxBus: RxBusWrapper
private lateinit var dateUtil: DateUtil
private lateinit var testPumpPlugin: TestPumpPlugin
private lateinit var hardLimits: HardLimits
private var okProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"sens\":[{\"time\":\"00:00\",\"value\":\"6\"},{\"time\":\"2:00\",\"value\":\"6.2\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var belowLimitValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.001\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var notAlignedBasalValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:30\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var notStartingAtZeroValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:30\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var noUnitsValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\"}"
private var wrongProfile = "{\"dia\":\"5\",\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var okProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"},{\"time\":\"2:00\",\"value\":\"110\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var belowLimitValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.001\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var notAlignedBasalValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:30\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var notStartingAtZeroValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:30\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
private var noUnitsValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\"}"
private var wrongProfile = "{\"dia\":\"3\",\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
//String profileStore = "{\"defaultProfile\":\"Default\",\"store\":{\"Default\":" + validProfile + "}}";
@Before
fun prepare() {
testPumpPlugin = TestPumpPlugin { AndroidInjector { } }
testPumpPlugin = TestPumpPlugin(HasAndroidInjector { AndroidInjector { } })
dateUtil = DateUtil(context)
rxBus = RxBusWrapper(TestAapsSchedulers())
hardLimits = HardLimits(aapsLogger, rxBus, sp, resourceHelper, context, repository)
`when`(activePluginProvider.activePump).thenReturn(testPumpPlugin)
`when`(resourceHelper.gs(R.string.profile_per_unit)).thenReturn("/U")
`when`(resourceHelper.gs(R.string.profile_carbs_per_unit)).thenReturn("g/U")
@ -72,10 +69,10 @@ class ProfileTest : TestBase() {
// Test valid profile
var p = ProfileSealed.Pure(pureProfileFromJson(JSONObject(okProfile), dateUtil)!!)
Assert.assertEquals(true, p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus, hardLimits).isValid)
Assert.assertEquals(true, p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus))
// Assert.assertEquals(true, p.log().contains("NS units: mmol"))
// JSONAssert.assertEquals(JSONObject(okProfile), p.toPureNsJson(dateUtil), false)
Assert.assertEquals(5.0, p.dia, 0.01)
Assert.assertEquals(3.0, p.dia, 0.01)
// Assert.assertEquals(TimeZone.getTimeZone("UTC"), p.timeZone)
Assert.assertEquals("00:30", dateUtil.format_HH_MM(30 * 60))
val c = Calendar.getInstance()
@ -83,13 +80,13 @@ class ProfileTest : TestBase() {
c[Calendar.MINUTE] = 0
c[Calendar.SECOND] = 0
c[Calendar.MILLISECOND] = 0
Assert.assertEquals(108.0, p.getIsfMgdl(c.timeInMillis), 0.01)
Assert.assertEquals(1800.0, p.getIsfMgdl(c.timeInMillis), 0.01)
c[Calendar.HOUR_OF_DAY] = 2
Assert.assertEquals(111.6, p.getIsfMgdl(c.timeInMillis), 0.01)
Assert.assertEquals(1980.0, p.getIsfMgdl(c.timeInMillis), 0.01)
// Assert.assertEquals(110.0, p.getIsfTimeFromMidnight(2 * 60 * 60), 0.01)
Assert.assertEquals("""
00:00 6,0 mmol/U
02:00 6,2 mmol/U
00:00 100,0 mmol/U
02:00 110,0 mmol/U
""".trimIndent(), p.getIsfList(resourceHelper, dateUtil).replace(".", ","))
Assert.assertEquals(30.0, p.getIc(c.timeInMillis), 0.01)
Assert.assertEquals(30.0, p.getIcTimeFromMidnight(2 * 60 * 60), 0.01)
@ -124,7 +121,7 @@ class ProfileTest : TestBase() {
//Test basal profile below limit
p = ProfileSealed.Pure(pureProfileFromJson(JSONObject(belowLimitValidProfile), dateUtil)!!)
p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus, hardLimits)
p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus)
// Test profile w/o units
Assert.assertNull(pureProfileFromJson(JSONObject(noUnitsValidProfile), dateUtil))
@ -142,21 +139,21 @@ class ProfileTest : TestBase() {
Assert.assertEquals(0.05, p.getBasal(c.timeInMillis), 0.01)
Assert.assertEquals(1.2, p.percentageBasalSum(), 0.01)
Assert.assertEquals(60.0, p.getIc(c.timeInMillis), 0.01)
Assert.assertEquals(223.2, p.getIsfMgdl(c.timeInMillis), 0.01)
Assert.assertEquals(3960.0, p.getIsfMgdl(c.timeInMillis), 0.01)
// Test timeshift functionality
p = ProfileSealed.Pure(pureProfileFromJson(JSONObject(okProfile), dateUtil)!!)
p.ts = 1
Assert.assertEquals(
"""
00:00 6.2 mmol/U
01:00 6.0 mmol/U
03:00 6.2 mmol/U
00:00 110.0 mmol/U
01:00 100.0 mmol/U
03:00 110.0 mmol/U
""".trimIndent(), p.getIsfList(resourceHelper, dateUtil))
// Test hour alignment
testPumpPlugin.pumpDescription.is30minBasalRatesCapable = false
p = ProfileSealed.Pure(pureProfileFromJson(JSONObject(notAlignedBasalValidProfile), dateUtil)!!)
p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus, hardLimits)
p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus)
}
}