Profile comparator

This commit is contained in:
Milos Kozak 2020-07-06 13:52:10 +02:00
parent ceaa3c8750
commit b91f8e668a
5 changed files with 180 additions and 27 deletions

View file

@ -8,9 +8,9 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile import info.nightscout.androidaps.data.defaultProfile.DefaultProfile
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.InstanceId import info.nightscout.androidaps.utils.InstanceId
@ -62,16 +62,19 @@ class SurveyActivity : NoSplashAppCompatActivity() {
ToastUtils.showToastInUiThread(this, R.string.invalidweight) ToastUtils.showToastInUiThread(this, R.string.invalidweight)
return@setOnClickListener return@setOnClickListener
} }
val profile = defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) profileFunction.getProfile()?.let { runningProfile ->
val args = Bundle() val profile = defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())
args.putLong("time", DateUtil.now()) ProfileViewerDialog().also { pvd ->
args.putInt("mode", ProfileViewerDialog.Mode.CUSTOM_PROFILE.ordinal) pvd.arguments = Bundle().also {
args.putString("customProfile", profile.data.toString()) it.putLong("time", DateUtil.now())
args.putString("customProfileUnits", profile.units) it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal)
args.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight") it.putString("customProfile", runningProfile.data.toString())
val pvd = ProfileViewerDialog() it.putString("customProfile2", profile.data.toString())
pvd.arguments = args it.putString("customProfileUnits", profile.units)
pvd.show(supportFragmentManager, "ProfileViewDialog") it.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight")
}
}.show(supportFragmentManager, "ProfileViewDialog")
}
} }
survey_submit.setOnClickListener { survey_submit.setOnClickListener {

View file

@ -370,7 +370,7 @@ public class Profile {
return lastValue; return lastValue;
} }
protected String format_HH_MM(Integer timeAsSeconds) { public static String format_HH_MM(Integer timeAsSeconds) {
String time; String time;
int hour = timeAsSeconds / 60 / 60; int hour = timeAsSeconds / 60 / 60;
int minutes = (timeAsSeconds - hour * 60 * 60) / 60; int minutes = (timeAsSeconds - hour * 60 * 60) / 60;

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.os.Bundle import android.os.Bundle
import android.text.Spanned
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -14,10 +15,12 @@ import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import kotlinx.android.synthetic.main.close.* import kotlinx.android.synthetic.main.close.*
import kotlinx.android.synthetic.main.dialog_profileviewer.* import kotlinx.android.synthetic.main.dialog_profileviewer.*
import org.json.JSONObject import org.json.JSONObject
import java.text.DecimalFormat
import javax.inject.Inject import javax.inject.Inject
class ProfileViewerDialog : DaggerDialogFragment() { class ProfileViewerDialog : DaggerDialogFragment() {
@ -32,11 +35,13 @@ class ProfileViewerDialog : DaggerDialogFragment() {
enum class Mode(val i: Int) { enum class Mode(val i: Int) {
RUNNING_PROFILE(1), RUNNING_PROFILE(1),
CUSTOM_PROFILE(2), CUSTOM_PROFILE(2),
DB_PROFILE(3) DB_PROFILE(3),
PROFILE_COMPARE(4)
} }
private var mode: Mode = Mode.RUNNING_PROFILE private var mode: Mode = Mode.RUNNING_PROFILE
private var customProfileJson: String = "" private var customProfileJson: String = ""
private var customProfileJson2: String = ""
private var customProfileName: String = "" private var customProfileName: String = ""
private var customProfileUnits: String = Constants.MGDL private var customProfileUnits: String = Constants.MGDL
@ -49,6 +54,8 @@ class ProfileViewerDialog : DaggerDialogFragment() {
customProfileJson = bundle.getString("customProfile", "") customProfileJson = bundle.getString("customProfile", "")
customProfileUnits = bundle.getString("customProfileUnits", Constants.MGDL) customProfileUnits = bundle.getString("customProfileUnits", Constants.MGDL)
customProfileName = bundle.getString("customProfileName", "") customProfileName = bundle.getString("customProfileName", "")
if (mode == Mode.PROFILE_COMPARE)
customProfileJson2 = bundle.getString("customProfile2", "")
} }
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
@ -65,11 +72,13 @@ class ProfileViewerDialog : DaggerDialogFragment() {
close.setOnClickListener { dismiss() } close.setOnClickListener { dismiss() }
val profile: Profile? val profile: Profile?
val profile2: Profile?
val profileName: String? val profileName: String?
val date: String? val date: String?
when (mode) { when (mode) {
Mode.RUNNING_PROFILE -> { Mode.RUNNING_PROFILE -> {
profile = activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.profileObject profile = activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.profileObject
profile2 = null
profileName = activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.customizedName profileName = activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.customizedName
date = dateUtil.dateAndTimeString(activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.date date = dateUtil.dateAndTimeString(activePlugin.activeTreatments.getProfileSwitchFromHistory(time)?.date
?: 0) ?: 0)
@ -78,6 +87,15 @@ class ProfileViewerDialog : DaggerDialogFragment() {
Mode.CUSTOM_PROFILE -> { Mode.CUSTOM_PROFILE -> {
profile = Profile(injector, JSONObject(customProfileJson), customProfileUnits) profile = Profile(injector, JSONObject(customProfileJson), customProfileUnits)
profile2 = null
profileName = customProfileName
date = ""
profileview_datelayout.visibility = View.GONE
}
Mode.PROFILE_COMPARE -> {
profile = Profile(injector, JSONObject(customProfileJson), customProfileUnits)
profile2 = Profile(injector, JSONObject(customProfileJson2), customProfileUnits)
profileName = customProfileName profileName = customProfileName
date = "" date = ""
profileview_datelayout.visibility = View.GONE profileview_datelayout.visibility = View.GONE
@ -86,6 +104,7 @@ class ProfileViewerDialog : DaggerDialogFragment() {
Mode.DB_PROFILE -> { Mode.DB_PROFILE -> {
val profileList = databaseHelper.getProfileSwitchData(time, true) val profileList = databaseHelper.getProfileSwitchData(time, true)
profile = if (profileList.isNotEmpty()) profileList[0].profileObject else null profile = if (profileList.isNotEmpty()) profileList[0].profileObject else null
profile2 = null
profileName = if (profileList.isNotEmpty()) profileList[0].customizedName else null profileName = if (profileList.isNotEmpty()) profileList[0].customizedName else null
date = if (profileList.isNotEmpty()) dateUtil.dateAndTimeString(profileList[0].date) else null date = if (profileList.isNotEmpty()) dateUtil.dateAndTimeString(profileList[0].date) else null
profileview_datelayout.visibility = View.VISIBLE profileview_datelayout.visibility = View.VISIBLE
@ -93,20 +112,38 @@ class ProfileViewerDialog : DaggerDialogFragment() {
} }
profileview_noprofile.visibility = View.VISIBLE profileview_noprofile.visibility = View.VISIBLE
profile?.let { if (mode == Mode.PROFILE_COMPARE)
profileview_units.text = it.units profile?.let { profile1 ->
profileview_dia.text = resourceHelper.gs(R.string.format_hours, it.dia) profile2?.let { profile2 ->
profileview_activeprofile.text = profileName profileview_units.text = profile1.units
profileview_date.text = date profileview_dia.text = HtmlHelper.fromHtml(formatColors("", profile1.dia, profile1.dia, DecimalFormat("0.00"), resourceHelper.gs(R.string.shorthour)))
profileview_ic.text = it.icList profileview_activeprofile.text = profileName
profileview_isf.text = it.isfList profileview_date.text = date
profileview_basal.text = it.basalList profileview_ic.text = ics(profile1, profile2)
profileview_target.text = it.targetList profileview_isf.text = isfs(profile1, profile2)
basal_graph.show(it) profileview_basal.text = basals(profile1, profile2)
profileview_target.text = HtmlHelper.fromHtml(formatColors("", profile1.targetList + "<br>", profile2.targetList, ""))
basal_graph.show(profile1, profile2)
}
profileview_noprofile.visibility = View.GONE profileview_noprofile.visibility = View.GONE
profileview_invalidprofile.visibility = if (it.isValid("ProfileViewDialog")) View.GONE else View.VISIBLE profileview_invalidprofile.visibility = if (profile1.isValid("ProfileViewDialog")) View.GONE else View.VISIBLE
} }
else
profile?.let {
profileview_units.text = it.units
profileview_dia.text = resourceHelper.gs(R.string.format_hours, it.dia)
profileview_activeprofile.text = profileName
profileview_date.text = date
profileview_ic.text = it.icList
profileview_isf.text = it.isfList
profileview_basal.text = it.basalList
profileview_target.text = it.targetList
basal_graph.show(it)
profileview_noprofile.visibility = View.GONE
profileview_invalidprofile.visibility = if (it.isValid("ProfileViewDialog")) View.GONE else View.VISIBLE
}
} }
override fun onStart() { override fun onStart() {
@ -121,6 +158,79 @@ class ProfileViewerDialog : DaggerDialogFragment() {
bundle.putString("customProfile", customProfileJson) bundle.putString("customProfile", customProfileJson)
bundle.putString("customProfileName", customProfileName) bundle.putString("customProfileName", customProfileName)
bundle.putString("customProfileUnits", customProfileUnits) bundle.putString("customProfileUnits", customProfileUnits)
if (mode == Mode.PROFILE_COMPARE)
bundle.putString("customProfile2", customProfileJson2)
} }
private fun formatColors(label: String, value1: Double, value2: Double, format: DecimalFormat, units: String): String {
return formatColors(label, format.format(value1), format.format(value2), units)
}
private fun formatColors(label: String, text1: String, text2: String, units: String): String {
var s = "<font color='${resourceHelper.gc(R.color.white)}'>$label</font>"
s += " "
s += "<font color='${resourceHelper.gc(R.color.tempbasal)}'>$text1</font>"
s += " "
s += "<font color='${resourceHelper.gc(R.color.examinedProfile)}'>$text2</font>"
s += " "
s += "<font color='${resourceHelper.gc(R.color.white)}'>$units</font>"
return s
}
private fun basals(profile1: Profile, profile2: Profile): Spanned {
var prev1 = 0.0
var prev2 = 0.0
val s = StringBuilder()
for (hour in 0..23) {
val val1 = profile1.getBasalTimeFromMidnight(hour * 60 * 60)
val val2 = profile2.getBasalTimeFromMidnight(hour * 60 * 60)
if (val1 != prev1 || val2 != prev2) {
s.append(formatColors(Profile.format_HH_MM(hour * 60 * 60), val1, val2, DecimalFormat("0.00"), resourceHelper.gs(R.string.profile_ins_units_per_hour)))
s.append("<br>")
}
prev1 = val1
prev2 = val2
}
s.append(formatColors(
"",
profile1.baseBasalSum(),
profile2.baseBasalSum(),
DecimalFormat("0.00"),
resourceHelper.gs(R.string.insulin_unit_shortname)))
return HtmlHelper.fromHtml(s.toString())
}
private fun ics(profile1: Profile, profile2: Profile): Spanned {
var prev1 = 0.0
var prev2 = 0.0
val s = StringBuilder()
for (hour in 0..23) {
val val1 = profile1.getIcTimeFromMidnight(hour * 60 * 60)
val val2 = profile2.getIcTimeFromMidnight(hour * 60 * 60)
if (val1 != prev1 || val2 != prev2) {
s.append(formatColors(Profile.format_HH_MM(hour * 60 * 60), val1, val2, DecimalFormat("0.0"), resourceHelper.gs(R.string.profile_carbs_per_unit)))
s.append("<br>")
}
prev1 = val1
prev2 = val2
}
return HtmlHelper.fromHtml(s.toString())
}
private fun isfs(profile1: Profile, profile2: Profile): Spanned {
var prev1 = 0.0
var prev2 = 0.0
val s = StringBuilder()
for (hour in 0..23) {
val val1 = Profile.fromMgdlToUnits(profile1.getIsfMgdlTimeFromMidnight(hour * 60 * 60), profile1.units)
val val2 = Profile.fromMgdlToUnits(profile2.getIsfMgdlTimeFromMidnight(hour * 60 * 60), profile1.units)
if (val1 != prev1 || val2 != prev2) {
s.append(formatColors(Profile.format_HH_MM(hour * 60 * 60), val1, val2, DecimalFormat("0.0"), resourceHelper.gs(R.string.profile_carbs_per_unit)))
s.append("<br>")
}
prev1 = val1
prev2 = val2
}
return HtmlHelper.fromHtml(s.toString())
}
} }

View file

@ -1,13 +1,16 @@
package info.nightscout.androidaps.plugins.treatments.fragments package info.nightscout.androidaps.plugins.treatments.fragments
import android.content.Context import android.content.Context
import android.graphics.Color
import android.util.AttributeSet import android.util.AttributeSet
import com.jjoe64.graphview.GraphView import com.jjoe64.graphview.GraphView
import com.jjoe64.graphview.series.DataPoint import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries import com.jjoe64.graphview.series.LineGraphSeries
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.utils.Round
import java.util.* import java.util.*
import kotlin.math.max
class ProfileGraph : GraphView { class ProfileGraph : GraphView {
@ -21,7 +24,7 @@ class ProfileGraph : GraphView {
basalArray.add(DataPoint(hour.toDouble(), profile.getBasalTimeFromMidnight(hour * 60 * 60))) basalArray.add(DataPoint(hour.toDouble(), profile.getBasalTimeFromMidnight(hour * 60 * 60)))
basalArray.add(DataPoint((hour + 1).toDouble(), profile.getBasalTimeFromMidnight(hour * 60 * 60))) basalArray.add(DataPoint((hour + 1).toDouble(), profile.getBasalTimeFromMidnight(hour * 60 * 60)))
} }
val basalDataPoints: Array<DataPoint> = Array(basalArray.size){ i-> basalArray[i]} val basalDataPoints: Array<DataPoint> = Array(basalArray.size) { i -> basalArray[i] }
val basalSeries: LineGraphSeries<DataPoint> = LineGraphSeries(basalDataPoints) val basalSeries: LineGraphSeries<DataPoint> = LineGraphSeries(basalDataPoints)
addSeries(basalSeries) addSeries(basalSeries)
basalSeries.thickness = 8 basalSeries.thickness = 8
@ -35,4 +38,40 @@ class ProfileGraph : GraphView {
gridLabelRenderer.numHorizontalLabels = 13 gridLabelRenderer.numHorizontalLabels = 13
gridLabelRenderer.verticalLabelsColor = basalSeries.color gridLabelRenderer.verticalLabelsColor = basalSeries.color
} }
fun show(profile1: Profile, profile2: Profile) {
removeAllSeries()
// profile 1
val basalArray1: MutableList<DataPoint> = ArrayList()
for (hour in 0..23) {
basalArray1.add(DataPoint(hour.toDouble(), profile1.getBasalTimeFromMidnight(hour * 60 * 60)))
basalArray1.add(DataPoint((hour + 1).toDouble(), profile1.getBasalTimeFromMidnight(hour * 60 * 60)))
}
val basalSeries1: LineGraphSeries<DataPoint> = LineGraphSeries(Array(basalArray1.size) { i -> basalArray1[i] })
addSeries(basalSeries1)
basalSeries1.thickness = 8
basalSeries1.isDrawBackground = true
// profile 2
val basalArray2: MutableList<DataPoint> = ArrayList()
for (hour in 0..23) {
basalArray2.add(DataPoint(hour.toDouble(), profile2.getBasalTimeFromMidnight(hour * 60 * 60)))
basalArray2.add(DataPoint((hour + 1).toDouble(), profile2.getBasalTimeFromMidnight(hour * 60 * 60)))
}
val basalSeries2: LineGraphSeries<DataPoint> = LineGraphSeries(Array(basalArray2.size) { i -> basalArray2[i] })
addSeries(basalSeries2)
basalSeries2.thickness = 8
basalSeries2.isDrawBackground = false
basalSeries2.color = context.getColor(R.color.examinedProfile)
basalSeries2.backgroundColor = context.getColor(R.color.examinedProfile)
viewport.isXAxisBoundsManual = true
viewport.setMinX(0.0)
viewport.setMaxX(24.0)
viewport.isYAxisBoundsManual = true
viewport.setMinY(0.0)
viewport.setMaxY(Round.ceilTo(max(profile1.maxDailyBasal, profile2.maxDailyBasal) * 1.1, 0.5))
gridLabelRenderer.numHorizontalLabels = 13
}
} }

View file

@ -23,6 +23,7 @@
<color name="warningAlertHeaderText">#FF000000</color> <color name="warningAlertHeaderText">#FF000000</color>
<color name="errorAlertBackground">#FFFF5555</color> <color name="errorAlertBackground">#FFFF5555</color>
<color name="errorAlertHeaderText">#FF000000</color> <color name="errorAlertHeaderText">#FF000000</color>
<color name="examinedProfile">#FFFF5555</color>
<!-- Treatment--> <!-- Treatment-->
<color name="tempbasal">#C803A9F4</color> <color name="tempbasal">#C803A9F4</color>