|
@@ -1,9 +1,3 @@
|
|
|
-//
|
|
|
|
|
-// FilteredLoopsView.swift
|
|
|
|
|
-// FreeAPS
|
|
|
|
|
-//
|
|
|
|
|
-// Created by Jon Mårtensson on 2023-05-29.
|
|
|
|
|
-//
|
|
|
|
|
import Charts
|
|
import Charts
|
|
|
import CoreData
|
|
import CoreData
|
|
|
import SwiftDate
|
|
import SwiftDate
|
|
@@ -25,7 +19,18 @@ struct ChartsView: View {
|
|
|
var body: some View {
|
|
var body: some View {
|
|
|
glucoseChart
|
|
glucoseChart
|
|
|
Rectangle().fill(.cyan.opacity(0.2)).frame(maxHeight: 3)
|
|
Rectangle().fill(.cyan.opacity(0.2)).frame(maxHeight: 3)
|
|
|
- if standing { tirChart } else { standingTIRchart }
|
|
|
|
|
|
|
+ if standing {
|
|
|
|
|
+ VStack {
|
|
|
|
|
+ tirChart
|
|
|
|
|
+ Rectangle().fill(.cyan.opacity(0.2)).frame(maxHeight: 3)
|
|
|
|
|
+ groupedGlucoseStatsLaying
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ HStack(spacing: 20) {
|
|
|
|
|
+ standingTIRchart
|
|
|
|
|
+ groupedGlucose
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
init(
|
|
init(
|
|
@@ -96,12 +101,11 @@ struct ChartsView: View {
|
|
|
units == .mmolL ? 15 : 270
|
|
units == .mmolL ? 15 : 270
|
|
|
]
|
|
]
|
|
|
)
|
|
)
|
|
|
- } // .background(.gray.opacity(0.05))
|
|
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var tirChart: some View {
|
|
var tirChart: some View {
|
|
|
let fetched = tir()
|
|
let fetched = tir()
|
|
|
-
|
|
|
|
|
let low = lowLimit * (units == .mmolL ? Decimal(conversionFactor) : 1)
|
|
let low = lowLimit * (units == .mmolL ? Decimal(conversionFactor) : 1)
|
|
|
let high = highLimit * (units == .mmolL ? Decimal(conversionFactor) : 1)
|
|
let high = highLimit * (units == .mmolL ? Decimal(conversionFactor) : 1)
|
|
|
|
|
|
|
@@ -110,7 +114,7 @@ struct ChartsView: View {
|
|
|
type: NSLocalizedString(
|
|
type: NSLocalizedString(
|
|
|
"Low",
|
|
"Low",
|
|
|
comment: ""
|
|
comment: ""
|
|
|
- ) + " (\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
|
|
|
|
|
|
|
+ ) + " (≤\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
|
|
|
percent: fetched[0].decimal
|
|
percent: fetched[0].decimal
|
|
|
),
|
|
),
|
|
|
.init(type: NSLocalizedString("In Range", comment: ""), percent: fetched[1].decimal),
|
|
.init(type: NSLocalizedString("In Range", comment: ""), percent: fetched[1].decimal),
|
|
@@ -118,7 +122,7 @@ struct ChartsView: View {
|
|
|
type: NSLocalizedString(
|
|
type: NSLocalizedString(
|
|
|
"High",
|
|
"High",
|
|
|
comment: ""
|
|
comment: ""
|
|
|
- ) + " (\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
|
|
|
|
|
|
|
+ ) + " (≥\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
|
|
|
percent: fetched[2].decimal
|
|
percent: fetched[2].decimal
|
|
|
)
|
|
)
|
|
|
]
|
|
]
|
|
@@ -138,12 +142,12 @@ struct ChartsView: View {
|
|
|
NSLocalizedString(
|
|
NSLocalizedString(
|
|
|
"Low",
|
|
"Low",
|
|
|
comment: ""
|
|
comment: ""
|
|
|
- ) + " (\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .red,
|
|
|
|
|
|
|
+ ) + " (≤\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .red,
|
|
|
NSLocalizedString("In Range", comment: ""): .green,
|
|
NSLocalizedString("In Range", comment: ""): .green,
|
|
|
NSLocalizedString(
|
|
NSLocalizedString(
|
|
|
"High",
|
|
"High",
|
|
|
comment: ""
|
|
comment: ""
|
|
|
- ) + " (\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
|
|
|
|
|
|
|
+ ) + " (≥\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
|
|
|
]).frame(maxHeight: 25)
|
|
]).frame(maxHeight: 25)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -151,48 +155,131 @@ struct ChartsView: View {
|
|
|
let fetched = tir()
|
|
let fetched = tir()
|
|
|
let low = lowLimit * (units == .mmolL ? Decimal(conversionFactor) : 1)
|
|
let low = lowLimit * (units == .mmolL ? Decimal(conversionFactor) : 1)
|
|
|
let high = highLimit * (units == .mmolL ? Decimal(conversionFactor) : 1)
|
|
let high = highLimit * (units == .mmolL ? Decimal(conversionFactor) : 1)
|
|
|
|
|
+ let fraction = units == .mmolL ? 1 : 0
|
|
|
let data: [ShapeModel] = [
|
|
let data: [ShapeModel] = [
|
|
|
.init(
|
|
.init(
|
|
|
type: NSLocalizedString(
|
|
type: NSLocalizedString(
|
|
|
"Low",
|
|
"Low",
|
|
|
comment: ""
|
|
comment: ""
|
|
|
- ) + " (\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
|
|
|
|
|
|
|
+ ) + " (≤ \(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
|
|
|
percent: fetched[0].decimal
|
|
percent: fetched[0].decimal
|
|
|
),
|
|
),
|
|
|
- .init(type: NSLocalizedString("In Range", comment: ""), percent: fetched[1].decimal),
|
|
|
|
|
|
|
+ .init(
|
|
|
|
|
+ type: "> \(low.formatted(.number.precision(.fractionLength(fraction)))) - < \(high.formatted(.number.precision(.fractionLength(fraction))))",
|
|
|
|
|
+ percent: fetched[1].decimal
|
|
|
|
|
+ ),
|
|
|
.init(
|
|
.init(
|
|
|
type: NSLocalizedString(
|
|
type: NSLocalizedString(
|
|
|
"High",
|
|
"High",
|
|
|
comment: ""
|
|
comment: ""
|
|
|
- ) + " (\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
|
|
|
|
|
|
|
+ ) + " (≥ \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))",
|
|
|
percent: fetched[2].decimal
|
|
percent: fetched[2].decimal
|
|
|
)
|
|
)
|
|
|
]
|
|
]
|
|
|
|
|
+ return Chart(data) { shape in
|
|
|
|
|
+ BarMark(
|
|
|
|
|
+ x: .value("Shape", shape.type),
|
|
|
|
|
+ y: .value("Percentage", shape.percent)
|
|
|
|
|
+ )
|
|
|
|
|
+ .foregroundStyle(by: .value("Group", shape.type))
|
|
|
|
|
+ .annotation(position: shape.percent > 19 ? .overlay : .automatic, alignment: .center) {
|
|
|
|
|
+ Text(shape.percent == 0 ? "" : "\(shape.percent, format: .number.precision(.fractionLength(0)))")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .chartXAxis(.hidden)
|
|
|
|
|
+ .chartYAxis {
|
|
|
|
|
+ AxisMarks(
|
|
|
|
|
+ format: Decimal.FormatStyle.Percent.percent.scale(1)
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ .chartForegroundStyleScale([
|
|
|
|
|
+ NSLocalizedString(
|
|
|
|
|
+ "Low",
|
|
|
|
|
+ comment: ""
|
|
|
|
|
+ ) + " (≤ \(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .red,
|
|
|
|
|
+ "> \(low.formatted(.number.precision(.fractionLength(fraction)))) - < \(high.formatted(.number.precision(.fractionLength(fraction))))": .green,
|
|
|
|
|
+ NSLocalizedString(
|
|
|
|
|
+ "High",
|
|
|
|
|
+ comment: ""
|
|
|
|
|
+ ) + " (≥ \(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
|
|
|
|
|
+ ])
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return VStack(alignment: .center) {
|
|
|
|
|
- Chart(data) { shape in
|
|
|
|
|
- BarMark(
|
|
|
|
|
- x: .value("Shape", shape.type),
|
|
|
|
|
- y: .value("Percentage", shape.percent)
|
|
|
|
|
- )
|
|
|
|
|
- .foregroundStyle(by: .value("Group", shape.type))
|
|
|
|
|
- .annotation(position: shape.percent <= 9 ? .top : .overlay, alignment: .center) {
|
|
|
|
|
- Text(shape.percent == 0 ? "" : "\(shape.percent, format: .number.precision(.fractionLength(0))) %")
|
|
|
|
|
|
|
+ var groupedGlucose: some View {
|
|
|
|
|
+ VStack(alignment: .leading, spacing: 20) {
|
|
|
|
|
+ let glucose = fetchRequest
|
|
|
|
|
+ let mapGlucose = glucose.compactMap({ each in each.glucose })
|
|
|
|
|
+ if !mapGlucose.isEmpty {
|
|
|
|
|
+ let mapGlucoseAcuteLow = mapGlucose.filter({ $0 < Int16(3.3 / 0.0555) })
|
|
|
|
|
+ let mapGlucoseHigh = mapGlucose.filter({ $0 > Int16(11 / 0.0555) })
|
|
|
|
|
+ let mapGlucoseNormal = mapGlucose.filter({ $0 > Int16(3.8 / 0.0555) && $0 < Int16(7.9 / 0.0555) })
|
|
|
|
|
+
|
|
|
|
|
+ HStack {
|
|
|
|
|
+ let value = Double(mapGlucoseHigh.count * 100 / mapGlucose.count)
|
|
|
|
|
+ if value != 0 {
|
|
|
|
|
+ Text(units == .mmolL ? "> 11 " : "> 200 ").foregroundColor(.secondary)
|
|
|
|
|
+ Text(value.formatted()).foregroundColor(.orange)
|
|
|
|
|
+ Text("%").foregroundColor(.secondary)
|
|
|
|
|
+ }
|
|
|
|
|
+ }.font(.caption)
|
|
|
|
|
+
|
|
|
|
|
+ HStack {
|
|
|
|
|
+ let value = Double(mapGlucoseNormal.count * 100 / mapGlucose.count)
|
|
|
|
|
+ if value != 0 {
|
|
|
|
|
+ Text(units == .mmolL ? "3.9-7.8" : "70-140").foregroundColor(.secondary)
|
|
|
|
|
+ Text(value.formatted()).foregroundColor(.green)
|
|
|
|
|
+ Text("%").foregroundColor(.secondary)
|
|
|
|
|
+ }
|
|
|
|
|
+ }.font(.caption)
|
|
|
|
|
+
|
|
|
|
|
+ HStack {
|
|
|
|
|
+ let value = Double(mapGlucoseAcuteLow.count * 100 / mapGlucose.count)
|
|
|
|
|
+ if value != 0 {
|
|
|
|
|
+ Text(units == .mmolL ? "< 3.3 " : "< 59 ").foregroundColor(.secondary)
|
|
|
|
|
+ Text(value.formatted()).foregroundColor(.red)
|
|
|
|
|
+ Text("%").foregroundColor(.secondary)
|
|
|
|
|
+ }
|
|
|
|
|
+ }.font(.caption)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var groupedGlucoseStatsLaying: some View {
|
|
|
|
|
+ HStack {
|
|
|
|
|
+ let glucose = fetchRequest
|
|
|
|
|
+ let mapGlucose = glucose.compactMap({ each in each.glucose })
|
|
|
|
|
+ if !mapGlucose.isEmpty {
|
|
|
|
|
+ let mapGlucoseLow = mapGlucose.filter({ $0 < Int16(3.3 / 0.0555) })
|
|
|
|
|
+ let mapGlucoseNormal = mapGlucose.filter({ $0 > Int16(3.8 / 0.0555) && $0 < Int16(7.9 / 0.0555) })
|
|
|
|
|
+ let mapGlucoseAcuteHigh = mapGlucose.filter({ $0 > Int16(11 / 0.0555) })
|
|
|
|
|
+
|
|
|
|
|
+ HStack {
|
|
|
|
|
+ let value = Double(mapGlucoseLow.count * 100 / mapGlucose.count)
|
|
|
|
|
+ if value != 0 {
|
|
|
|
|
+ Text(units == .mmolL ? "< 3.3" : "< 59").font(.caption2).foregroundColor(.secondary)
|
|
|
|
|
+ Text(value.formatted()).font(.caption).foregroundColor(value == 0 ? .green : .red)
|
|
|
|
|
+ Text("%").font(.caption)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Spacer()
|
|
|
|
|
+ HStack {
|
|
|
|
|
+ let value = Double(mapGlucoseNormal.count * 100 / mapGlucose.count)
|
|
|
|
|
+ if value != 0 {
|
|
|
|
|
+ Text(units == .mmolL ? "3.9-7.8" : "70-140").foregroundColor(.secondary)
|
|
|
|
|
+ Text(value.formatted()).foregroundColor(.green)
|
|
|
|
|
+ Text("%").foregroundColor(.secondary)
|
|
|
|
|
+ }
|
|
|
|
|
+ }.font(.caption)
|
|
|
|
|
+ Spacer()
|
|
|
|
|
+ HStack {
|
|
|
|
|
+ let value = Double(mapGlucoseAcuteHigh.count * 100 / mapGlucose.count)
|
|
|
|
|
+ if value != 0 {
|
|
|
|
|
+ Text(units == .mmolL ? "> 11.0" : "> 216").font(.caption).foregroundColor(.secondary)
|
|
|
|
|
+ Text(value.formatted()).font(.caption).foregroundColor(value == 0 ? .green : .orange)
|
|
|
|
|
+ Text("%").font(.caption)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- .chartYAxis(.hidden)
|
|
|
|
|
- .chartLegend(.hidden)
|
|
|
|
|
- .chartForegroundStyleScale([
|
|
|
|
|
- NSLocalizedString(
|
|
|
|
|
- "Low",
|
|
|
|
|
- comment: ""
|
|
|
|
|
- ) + " (\(low.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .red,
|
|
|
|
|
- NSLocalizedString("In Range", comment: ""): .green,
|
|
|
|
|
- NSLocalizedString(
|
|
|
|
|
- "High",
|
|
|
|
|
- comment: ""
|
|
|
|
|
- ) + " (\(high.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))))": .orange
|
|
|
|
|
- ])
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|