Skip to content

Commit aebfec8

Browse files
committed
Added routes and converters for additional oura endpoints
1 parent 06a33af commit aebfec8

11 files changed

+360
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.radarbase.oura.converter
2+
3+
import com.fasterxml.jackson.databind.JsonNode
4+
import org.radarbase.oura.user.User
5+
import org.radarcns.connector.oura.OuraDailyCardiovascularAge
6+
import org.slf4j.LoggerFactory
7+
import java.time.Instant
8+
import java.time.OffsetDateTime
9+
10+
class OuraDailyCardiovascularAgeConverter(
11+
private val topic: String = "connect_oura_daily_cardiovascular_age",
12+
) : OuraDataConverter {
13+
override fun processRecords(
14+
root: JsonNode,
15+
user: User,
16+
): Sequence<Result<TopicData>> {
17+
val array = root.get("data")
18+
?: return emptySequence()
19+
return array.asSequence()
20+
.mapCatching {
21+
val startTime = OffsetDateTime.parse(it["timestamp"].textValue())
22+
val startInstant = startTime.toInstant()
23+
TopicData(
24+
key = user.observationKey,
25+
topic = topic,
26+
offset = startInstant.toEpoch(),
27+
value = it.toDailyCardiovascularAge(startInstant),
28+
)
29+
}
30+
}
31+
32+
private fun JsonNode.toDailyCardiovascularAge(
33+
startTime: Instant,
34+
): OuraDailyCardiovascularAge {
35+
val data = this
36+
return OuraDailyCardiovascularAge.newBuilder().apply {
37+
time = startTime.toEpochMilli() / 1000.0
38+
timeReceived = System.currentTimeMillis() / 1000.0
39+
id = data.get("id")?.textValue()
40+
day = data.get("day")?.textValue()
41+
vascularAge = data.get("vascular_age")?.intValue()
42+
}.build()
43+
}
44+
45+
companion object {
46+
val logger = LoggerFactory.getLogger(OuraDailyCardiovascularAgeConverter::class.java)
47+
}
48+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.radarbase.oura.converter
2+
3+
import com.fasterxml.jackson.databind.JsonNode
4+
import org.radarbase.oura.user.User
5+
import org.radarcns.connector.oura.OuraDailyResilience
6+
import org.radarcns.connector.oura.OuraResilienceLevel
7+
import org.slf4j.LoggerFactory
8+
import java.time.Instant
9+
import java.time.OffsetDateTime
10+
11+
class OuraDailyResilienceConverter(
12+
private val topic: String = "connect_oura_daily_resilience",
13+
) : OuraDataConverter {
14+
override fun processRecords(
15+
root: JsonNode,
16+
user: User,
17+
): Sequence<Result<TopicData>> {
18+
val array = root.get("data")
19+
?: return emptySequence()
20+
return array.asSequence()
21+
.mapCatching {
22+
val startTime = OffsetDateTime.parse(it["timestamp"].textValue())
23+
val startInstant = startTime.toInstant()
24+
TopicData(
25+
key = user.observationKey,
26+
topic = topic,
27+
offset = startInstant.toEpoch(),
28+
value = it.toDailyResilience(startInstant),
29+
)
30+
}
31+
}
32+
33+
private fun JsonNode.toDailyResilience(
34+
startTime: Instant,
35+
): OuraDailyResilience {
36+
val data = this
37+
return OuraDailyResilience.newBuilder().apply {
38+
time = startTime.toEpochMilli() / 1000.0
39+
timeReceived = System.currentTimeMillis() / 1000.0
40+
id = data.get("id")?.textValue()
41+
day = data.get("day")?.textValue()
42+
contributorSleepRecovery = data.get("contributors")?.get("sleep_recovery")?.floatValue()
43+
contributorDaytimeRecovery = data.get("contributors")?.get("daytime_recovery")?.floatValue()
44+
contributorStress = data.get("contributors")?.get("stress")?.floatValue()
45+
level = data.get("level")?.textValue()?.classifyResilienceLevel() ?: OuraResilienceLevel.UNKNOWN
46+
}.build()
47+
}
48+
49+
private fun String.classifyResilienceLevel(): OuraResilienceLevel {
50+
return when (this) {
51+
"limited" -> OuraResilienceLevel.LIMITED
52+
"adequate" -> OuraResilienceLevel.ADEQUATE
53+
"solid" -> OuraResilienceLevel.SOLID
54+
"strong" -> OuraResilienceLevel.STRONG
55+
"exceptional" -> OuraResilienceLevel.EXCEPTIONAL
56+
else -> OuraResilienceLevel.UNKNOWN
57+
}
58+
}
59+
60+
companion object {
61+
val logger = LoggerFactory.getLogger(OuraDailyResilienceConverter::class.java)
62+
}
63+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.radarbase.oura.converter
2+
3+
import com.fasterxml.jackson.databind.JsonNode
4+
import org.radarbase.oura.user.User
5+
import org.radarcns.connector.oura.OuraDailyStress
6+
import org.radarcns.connector.oura.OuraDaySummaryType
7+
import org.slf4j.LoggerFactory
8+
import java.time.Instant
9+
import java.time.OffsetDateTime
10+
11+
class OuraDailyStressConverter(
12+
private val topic: String = "connect_oura_daily_stress",
13+
) : OuraDataConverter {
14+
override fun processRecords(
15+
root: JsonNode,
16+
user: User,
17+
): Sequence<Result<TopicData>> {
18+
val array = root.get("data")
19+
?: return emptySequence()
20+
return array.asSequence()
21+
.mapCatching {
22+
val startTime = OffsetDateTime.parse(it["timestamp"].textValue())
23+
val startInstant = startTime.toInstant()
24+
TopicData(
25+
key = user.observationKey,
26+
topic = topic,
27+
offset = startInstant.toEpoch(),
28+
value = it.toDailyStress(startInstant),
29+
)
30+
}
31+
}
32+
33+
private fun JsonNode.toDailyStress(
34+
startTime: Instant,
35+
): OuraDailyStress {
36+
val data = this
37+
return OuraDailyStress.newBuilder().apply {
38+
time = startTime.toEpochMilli() / 1000.0
39+
timeReceived = System.currentTimeMillis() / 1000.0
40+
id = data.get("id")?.textValue()
41+
day = data.get("day")?.textValue()
42+
stressHigh = data.get("stress_high")?.intValue()
43+
recoveryHigh = data.get("recovery_high")?.intValue()
44+
daySummary = data.get("day_summary")?.textValue()?.classifyDaySummaryType()
45+
}.build()
46+
}
47+
48+
private fun String.classifyDaySummaryType(): OuraDaySummaryType {
49+
return when (this.lowercase()) {
50+
"normal" -> OuraDaySummaryType.NORMAL
51+
"stressful" -> OuraDaySummaryType.STRESSFUL
52+
"restored" -> OuraDaySummaryType.RESTORED
53+
else -> OuraDaySummaryType.UNKNOWN
54+
}
55+
}
56+
57+
companion object {
58+
val logger = LoggerFactory.getLogger(OuraDailyStressConverter::class.java)
59+
}
60+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.radarbase.oura.converter
2+
3+
import com.fasterxml.jackson.databind.JsonNode
4+
import org.radarbase.oura.user.User
5+
import org.radarcns.connector.oura.OuraEnhancedTag
6+
import org.slf4j.LoggerFactory
7+
import java.time.Instant
8+
import java.time.OffsetDateTime
9+
10+
class OuraEnhancedTagConverter(
11+
private val topic: String = "connect_oura_enhanced_tag",
12+
) : OuraDataConverter {
13+
override fun processRecords(
14+
root: JsonNode,
15+
user: User,
16+
): Sequence<Result<TopicData>> {
17+
val array = root.get("data")
18+
?: return emptySequence()
19+
return array.asSequence()
20+
.mapCatching {
21+
val startTime = OffsetDateTime.parse(it["start_time"].textValue())
22+
val startInstant = startTime.toInstant()
23+
TopicData(
24+
key = user.observationKey,
25+
topic = topic,
26+
offset = startInstant.toEpoch(),
27+
value = it.toEnhancedTag(startInstant),
28+
)
29+
}
30+
}
31+
32+
private fun JsonNode.toEnhancedTag(
33+
startTime: Instant,
34+
): OuraEnhancedTag {
35+
val data = this
36+
return OuraEnhancedTag.newBuilder().apply {
37+
time = startTime.toEpochMilli() / 1000.0
38+
timeReceived = System.currentTimeMillis() / 1000.0
39+
id = data.get("id")?.textValue()
40+
tagTypeCode = data.get("tag_type_code")?.textValue()
41+
tagStartTime = data.get("start_time")?.textValue()?.let {
42+
OffsetDateTime.parse(it).toInstant().toEpochMilli() / 1000.0
43+
}
44+
tagEndTime = data.get("end_time")?.textValue()?.let {
45+
OffsetDateTime.parse(it).toInstant().toEpochMilli() / 1000.0
46+
}
47+
startDay = data.get("start_day")?.textValue()
48+
endDay = data.get("end_day")?.textValue()
49+
comment = data.get("comment")?.textValue()
50+
customName = data.get("custom_name")?.textValue()
51+
}.build()
52+
}
53+
54+
companion object {
55+
val logger = LoggerFactory.getLogger(OuraEnhancedTagConverter::class.java)
56+
}
57+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.radarbase.oura.converter
2+
3+
import com.fasterxml.jackson.databind.JsonNode
4+
import org.radarbase.oura.user.User
5+
import org.radarcns.connector.oura.OuraVO2Max
6+
import org.slf4j.LoggerFactory
7+
import java.time.Instant
8+
import java.time.OffsetDateTime
9+
10+
class OuraVO2MaxConverter(
11+
private val topic: String = "connect_oura_vo2_max",
12+
) : OuraDataConverter {
13+
override fun processRecords(
14+
root: JsonNode,
15+
user: User,
16+
): Sequence<Result<TopicData>> {
17+
val array = root.get("data")
18+
?: return emptySequence()
19+
return array.asSequence()
20+
.mapCatching {
21+
val startTime = OffsetDateTime.parse(it["timestamp"].textValue())
22+
val startInstant = startTime.toInstant()
23+
TopicData(
24+
key = user.observationKey,
25+
topic = topic,
26+
offset = startInstant.toEpoch(),
27+
value = it.toVO2Max(startInstant),
28+
)
29+
}
30+
}
31+
32+
private fun JsonNode.toVO2Max(
33+
startTime: Instant,
34+
): OuraVO2Max {
35+
val data = this
36+
return OuraVO2Max.newBuilder().apply {
37+
time = startTime.toEpochMilli() / 1000.0
38+
timeReceived = System.currentTimeMillis() / 1000.0
39+
id = data.get("id")?.textValue()
40+
day = data.get("day")?.textValue()
41+
vo2Max = data.get("vo2_max")?.floatValue()
42+
timestamp = data.get("timestamp")?.textValue()?.let {
43+
OffsetDateTime.parse(it).toInstant().toEpochMilli()
44+
}
45+
}.build()
46+
}
47+
48+
companion object {
49+
val logger = LoggerFactory.getLogger(OuraDailyStressConverter::class.java)
50+
}
51+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.radarbase.oura.route
2+
3+
import org.radarbase.oura.converter.OuraDailyCardiovascularAgeConverter
4+
import org.radarbase.oura.user.UserRepository
5+
6+
class OuraDailyCardiovascularAgeRoute(
7+
private val userRepository: UserRepository,
8+
) : OuraRoute(userRepository) {
9+
10+
override fun subPath(): String = "daily_cardiovascular_age"
11+
12+
override fun toString(): String = "oura_daily_cardiovascular_age"
13+
14+
override var converters = listOf(OuraDailyCardiovascularAgeConverter())
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
package org.radarbase.oura.route
3+
4+
import org.radarbase.oura.converter.OuraDailyResilienceConverter
5+
import org.radarbase.oura.user.UserRepository
6+
7+
class OuraDailyResilienceRoute(
8+
private val userRepository: UserRepository,
9+
) : OuraRoute(userRepository) {
10+
11+
override fun subPath(): String = "daily_resilience"
12+
13+
override fun toString(): String = "oura_daily_resilience"
14+
15+
override var converters = listOf(OuraDailyResilienceConverter())
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.radarbase.oura.route
2+
3+
import org.radarbase.oura.converter.OuraDailyStressConverter
4+
import org.radarbase.oura.user.UserRepository
5+
6+
class OuraDailyStressRoute(
7+
private val userRepository: UserRepository,
8+
) : OuraRoute(userRepository) {
9+
10+
override fun subPath(): String = "daily_stress"
11+
12+
override fun toString(): String = "oura_daily_stress"
13+
14+
override var converters = listOf(OuraDailyStressConverter())
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.radarbase.oura.route
2+
3+
import org.radarbase.oura.converter.OuraEnhancedTagConverter
4+
import org.radarbase.oura.user.UserRepository
5+
6+
class OuraEnhancedTagRoute(
7+
private val userRepository: UserRepository,
8+
) : OuraRoute(userRepository) {
9+
10+
override fun subPath(): String = "enhanced_tag"
11+
12+
override fun toString(): String = "oura_enhanced_tag"
13+
14+
override var converters = listOf(OuraEnhancedTagConverter())
15+
}

oura-library/src/main/kotlin/org/radarbase/oura/route/OuraRouteFactory.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ object OuraRouteFactory {
1919
OuraRingConfigurationRoute(userRepository),
2020
OuraRestModePeriodRoute(userRepository),
2121
OuraSleepTimeRecommendationRoute(userRepository),
22+
OuraDailyStressRoute(userRepository),
23+
OuraDailyCardiovascularAgeRoute(userRepository),
24+
OuraDailyResilienceRoute(userRepository),
25+
OuraEnhancedTagRoute(userRepository),
26+
OuraVO2MaxRoute(userRepository)
2227
)
2328
}
2429
}

0 commit comments

Comments
 (0)