From b776b003c4db707f028ef61a37a50ba48cb453b4 Mon Sep 17 00:00:00 2001 From: Adam McNeilly Date: Sun, 1 Dec 2024 17:10:16 -0500 Subject: [PATCH 1/6] Adding debug http client setup. --- shared/app/build.gradle.kts | 2 + .../data/remote/HttpClientEngine.android.kt | 8 + .../shared/app/debug/GetEnv.android.kt | 5 + .../shared/app/data/remote/BaseKtorClient.kt | 25 +- .../app/data/remote/DefaultHttpClient.kt | 33 + .../app/data/remote/HttpClientEngine.kt | 5 + .../shared/app/debug/DebugHttpClient.kt | 10 + .../shared/app/debug/DebugHttpClientEngine.kt | 37 + .../shared/app/debug/DebugKtorClient.kt | 10 + .../pocketleague/shared/app/debug/GetEnv.kt | 8 + .../shared/app/debug/ReadTestData.kt | 19 + .../commonMain/resources/match_detail.json | 827 ++++++++++++++++++ .../src/commonMain/resources/match_list.json | 1 + .../app/data/remote/HttpClientEngine.ios.kt | 8 + .../shared/app/debug/GetEnv.ios.kt | 10 + 15 files changed, 984 insertions(+), 24 deletions(-) create mode 100644 shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.android.kt create mode 100644 shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.android.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/DefaultHttpClient.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClient.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugKtorClient.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadTestData.kt create mode 100644 shared/app/src/commonMain/resources/match_detail.json create mode 100644 shared/app/src/commonMain/resources/match_list.json create mode 100644 shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.ios.kt create mode 100644 shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.ios.kt diff --git a/shared/app/build.gradle.kts b/shared/app/build.gradle.kts index ac56e7559..1bae58ea6 100644 --- a/shared/app/build.gradle.kts +++ b/shared/app/build.gradle.kts @@ -36,7 +36,9 @@ kotlin { implementation(libs.ktor.client.content.negotiation) implementation(libs.ktor.client.core) implementation(libs.ktor.client.logging) + implementation(libs.ktor.client.testing) implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.square.okio) } commonTest.dependencies { diff --git a/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.android.kt b/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.android.kt new file mode 100644 index 000000000..d0a942cdb --- /dev/null +++ b/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.android.kt @@ -0,0 +1,8 @@ +package com.adammcneilly.pocketleague.shared.app.data.remote + +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.android.Android + +actual fun httpClientEngine(): HttpClientEngine { + return Android.create() +} diff --git a/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.android.kt b/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.android.kt new file mode 100644 index 000000000..f084da283 --- /dev/null +++ b/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.android.kt @@ -0,0 +1,5 @@ +package com.adammcneilly.pocketleague.shared.app.debug + +actual fun getEnv( + name: String, +): String? = System.getenv(name) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseKtorClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseKtorClient.kt index b167dc322..7ed1f8066 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseKtorClient.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseKtorClient.kt @@ -2,17 +2,9 @@ package com.adammcneilly.pocketleague.shared.app.data.remote import io.ktor.client.HttpClient import io.ktor.client.call.body -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger -import io.ktor.client.plugins.logging.Logging -import io.ktor.client.plugins.logging.SIMPLE import io.ktor.client.request.HttpRequestBuilder import io.ktor.client.request.get import io.ktor.client.request.parameter -import io.ktor.http.ContentType -import io.ktor.serialization.kotlinx.KotlinxSerializationConverter -import kotlinx.serialization.json.Json /** * Whenever we want to add params to a request, we just return a map of param @@ -29,23 +21,8 @@ typealias RemoteParams = Map */ open class BaseKtorClient( val baseURL: String, + val httpClient: HttpClient = defaultHttpClient(), ) { - val httpClient = HttpClient { - install(ContentNegotiation) { - val converter = KotlinxSerializationConverter( - Json { - ignoreUnknownKeys = true - }, - ) - register(ContentType.Any, converter) - } - - install(Logging) { - logger = Logger.SIMPLE - level = LogLevel.ALL - } - } - /** * A helper function to build the [baseURL] and [endpoint] operation and performs a get request. * Will also pass in the supplied [params] as necessary. diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/DefaultHttpClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/DefaultHttpClient.kt new file mode 100644 index 000000000..c457b3dca --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/DefaultHttpClient.kt @@ -0,0 +1,33 @@ +package com.adammcneilly.pocketleague.shared.app.data.remote + +import io.ktor.client.HttpClient +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.client.plugins.logging.SIMPLE +import io.ktor.http.ContentType +import io.ktor.serialization.kotlinx.KotlinxSerializationConverter +import kotlinx.serialization.json.Json + +/** + * A default implementation of an [HttpClient] that will use an engine defined + * by the platform it's being used on. This allows us to override for tests. + */ +fun defaultHttpClient( + engine: HttpClientEngine = httpClientEngine(), +) = HttpClient(engine) { + install(ContentNegotiation) { + val converter = KotlinxSerializationConverter( + Json { + ignoreUnknownKeys = true + }, + ) + register(ContentType.Any, converter) + } + install(Logging) { + logger = Logger.SIMPLE + level = LogLevel.ALL + } +} diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.kt new file mode 100644 index 000000000..3bbef3a58 --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.kt @@ -0,0 +1,5 @@ +package com.adammcneilly.pocketleague.shared.app.data.remote + +import io.ktor.client.engine.HttpClientEngine + +expect fun httpClientEngine(): HttpClientEngine diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClient.kt new file mode 100644 index 000000000..1fffc050a --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClient.kt @@ -0,0 +1,10 @@ +package com.adammcneilly.pocketleague.shared.app.debug + +import com.adammcneilly.pocketleague.shared.app.data.remote.defaultHttpClient + +/** + * Creates an implementation of our [defaultHttpClient] that uses a [mockEngine]. + */ +fun debugHttpClient( + responses: Map, +) = defaultHttpClient(debugHttpClientEngine(responses)) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt new file mode 100644 index 000000000..9ff54472a --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt @@ -0,0 +1,37 @@ +package com.adammcneilly.pocketleague.shared.app.debug + +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.fullPath +import io.ktor.http.headersOf +import io.ktor.utils.io.ByteReadChannel + +/** + * Create a custom [MockEngine] that will determine the response to return based on the supplied + * mock [responses]. + */ +fun debugHttpClientEngine( + responses: Map, +) = MockEngine { + val url = it.url.fullPath + + val response = responses[url] + + if (response == null) { + throw IllegalArgumentException( + """ + No mock response found for url: $url + Available urls: + ${responses.keys.joinToString("\n")} + """.trimIndent(), + ) + } + + respond( + content = ByteReadChannel(response), + status = HttpStatusCode.OK, + headers = headersOf(HttpHeaders.ContentType, "application/json"), + ) +} diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugKtorClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugKtorClient.kt new file mode 100644 index 000000000..2084479d6 --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugKtorClient.kt @@ -0,0 +1,10 @@ +package com.adammcneilly.pocketleague.shared.app.debug + +import com.adammcneilly.pocketleague.shared.app.data.remote.BaseKtorClient + +class DebugKtorClient( + mockResponses: Map = emptyMap(), +) : BaseKtorClient( + baseURL = "", + httpClient = debugHttpClient(mockResponses), + ) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.kt new file mode 100644 index 000000000..db3fab8cc --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.kt @@ -0,0 +1,8 @@ +package com.adammcneilly.pocketleague.shared.app.debug + +/** + * Retrieve an environment variable with a given [name]. + */ +expect fun getEnv( + name: String, +): String? diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadTestData.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadTestData.kt new file mode 100644 index 000000000..a3a3ce362 --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadTestData.kt @@ -0,0 +1,19 @@ +package com.adammcneilly.pocketleague.shared.app.debug + +import okio.FileSystem +import okio.Path.Companion.toPath +import okio.SYSTEM + +/** + * Reads the test data from a file with the given [fileName]. + */ +fun readTestData( + fileName: String, +): String { + val root = getEnv("TEST_DATA_ROOT")!! + val fullPath = "$root/$fileName".toPath() + + return FileSystem.SYSTEM.read(fullPath) { + readUtf8() + } +} diff --git a/shared/app/src/commonMain/resources/match_detail.json b/shared/app/src/commonMain/resources/match_detail.json new file mode 100644 index 000000000..4ef9e3eb8 --- /dev/null +++ b/shared/app/src/commonMain/resources/match_detail.json @@ -0,0 +1,827 @@ +{ + "_id": "638e412df73a2c40baef5e89", + "slug": "5e89-karmine-corp-vs-james-cheese", + "event": { + "_id": "632ef4e7da9d7ca1c7bb467c", + "slug": "467c-rlcs-2022-23-fall-major", + "name": "RLCS 2022-23 Fall Major", + "region": "INT", + "mode": 3, + "tier": "S", + "image": "https://griffon.octane.gg/events/rlcs-2022-23.png", + "groups": [ + "rlcs", + "rlcs2223", + "rlcs2223fall" + ] + }, + "stage": { + "_id": 0, + "name": "Swiss Stage", + "lan": true + }, + "date": "2022-12-08T13:30:00Z", + "format": { + "type": "best", + "length": 5 + }, + "blue": { + "score": 3, + "winner": true, + "team": { + "team": { + "_id": "60fbc5b887f814e9fbffdcbd", + "slug": "dcbd-karmine-corp", + "name": "Karmine Corp", + "region": "EU", + "image": "https://griffon.octane.gg/teams/Karmine_Corp.png", + "relevant": true + }, + "stats": { + "core": { + "shots": 16, + "goals": 5, + "saves": 13, + "assists": 1, + "score": 2835, + "shootingPercentage": 31.25 + }, + "boost": { + "bpm": 3714, + "bcpm": 3765.46186, + "avgAmount": 428.62, + "amountCollected": 20026, + "amountStolen": 4616, + "amountCollectedBig": 13645, + "amountStolenBig": 2728, + "amountCollectedSmall": 6381, + "amountStolenSmall": 1888, + "countCollectedBig": 162, + "countStolenBig": 31, + "countCollectedSmall": 548, + "countStolenSmall": 163, + "amountOverfill": 2612, + "amountOverfillStolen": 409, + "amountUsedWhileSupersonic": 2544, + "timeZeroBoost": 456.52, + "timeFullBoost": 290.83000000000004, + "timeBoost0To25": 1069.52, + "timeBoost25To50": 596.97, + "timeBoost50To75": 422.93000000000006, + "timeBoost75To100": 756.1700000000001 + }, + "movement": { + "totalDistance": 4350107, + "timeSupersonicSpeed": 511.52, + "timeBoostSpeed": 1099.5700000000002, + "timeSlowSpeed": 1300.9, + "timeGround": 1449.94, + "timeLowAir": 1225.18, + "timeHighAir": 236.87, + "timePowerslide": 53.72, + "countPowerslide": 466 + }, + "positioning": { + "timeDefensiveThird": 1247.47, + "timeNeutralThird": 945.49, + "timeOffensiveThird": 719.03, + "timeDefensiveHalf": 1726.44, + "timeOffensiveHalf": 1185.5700000000002, + "timeBehindBall": 2194.3999999999996, + "timeInfrontBall": 717.6 + }, + "demo": { + "inflicted": 10, + "taken": 10 + } + } + }, + "players": [ + { + "player": { + "_id": "5f3d8fdd95f40596eae24142", + "slug": "4142-exotiik", + "tag": "ExoTiiK", + "country": "fr" + }, + "stats": { + "core": { + "shots": 8, + "goals": 2, + "saves": 6, + "assists": 0, + "score": 1182, + "shootingPercentage": 25 + }, + "boost": { + "bpm": 1256, + "bcpm": 1288.97714, + "avgAmount": 140.02, + "amountCollected": 6860, + "amountStolen": 2260, + "amountCollectedBig": 4973, + "amountStolenBig": 1521, + "amountCollectedSmall": 1887, + "amountStolenSmall": 739, + "countCollectedBig": 58, + "countStolenBig": 18, + "countCollectedSmall": 162, + "countStolenSmall": 60, + "amountOverfill": 867, + "amountOverfillStolen": 287, + "amountUsedWhileSupersonic": 923, + "timeZeroBoost": 173.93, + "percentZeroBoost": 18.175121, + "timeFullBoost": 90.59, + "percentFullBoost": 9.438786566666666, + "timeBoost0To25": 365.99, + "timeBoost25To50": 208.57999999999998, + "timeBoost50To75": 131.02, + "timeBoost75To100": 231.95000000000002, + "percentBoost0To25": 39.148028333333336, + "percentBoost25To50": 22.338787666666665, + "percentBoost50To75": 13.915979, + "percentBoost75To100": 24.597204333333334 + }, + "movement": { + "avgSpeed": 4668, + "totalDistance": 1458835, + "timeSupersonicSpeed": 174.33, + "timeBoostSpeed": 380.15000000000003, + "timeSlowSpeed": 414.49, + "timeGround": 477.75, + "timeLowAir": 416.86, + "timeHighAir": 74.35, + "timePowerslide": 12.64, + "countPowerslide": 98, + "avgPowerslideDuration": 0.38, + "avgSpeedPercentage": 67.65217666666666, + "percentSlowSpeed": 42.71070233333334, + "percentBoostSpeed": 39.27831066666667, + "percentSupersonicSpeed": 18.010988, + "percentGround": 49.315131, + "percentLowAir": 43.01827599999999, + "percentHighAir": 7.6665909999999995 + }, + "positioning": { + "avgDistanceToBall": 8406, + "avgDistanceToBallPossession": 7787, + "avgDistanceToBallNoPossession": 9175, + "avgDistanceToMates": 11022, + "timeDefensiveThird": 381.28, + "timeNeutralThird": 285.38, + "timeOffensiveThird": 302.31, + "timeDefensiveHalf": 521.26, + "timeOffensiveHalf": 447.71999999999997, + "timeBehindBall": 659.55, + "timeInfrontBall": 309.43, + "timeMostBack": 260, + "timeMostForward": 423.8, + "goalsAgainstWhileLastDefender": 0, + "timeClosestToBall": 311.4, + "timeFarthestFromBall": 326.3, + "percentDefensiveThird": 39.381962, + "percentOffensiveThird": 31.157721999999996, + "percentNeutralThird": 29.46031566666667, + "percentDefensiveHalf": 53.832001999999996, + "percentOffensiveHalf": 46.168001333333336, + "percentBehindBall": 68.0937, + "percentInfrontBall": 31.90630566666667, + "percentMostBack": 27.193618333333333, + "percentMostForward": 44.22326, + "percentClosestToBall": 32.49067933333333, + "percentFarthestFromBall": 34.09113333333334 + }, + "demo": { + "inflicted": 6, + "taken": 4 + } + }, + "advanced": { + "goalParticipation": 40, + "rating": 0.881811553820493 + } + }, + { + "player": { + "_id": "5f5ae840c6cbf591c568a477", + "slug": "a477-itachi", + "tag": "itachi", + "country": "ma" + }, + "stats": { + "core": { + "shots": 5, + "goals": 1, + "saves": 4, + "assists": 1, + "score": 806, + "shootingPercentage": 20 + }, + "boost": { + "bpm": 1184, + "bcpm": 1169.35646, + "avgAmount": 128.49, + "amountCollected": 6219, + "amountStolen": 925, + "amountCollectedBig": 4039, + "amountStolenBig": 390, + "amountCollectedSmall": 2180, + "amountStolenSmall": 535, + "countCollectedBig": 47, + "countStolenBig": 4, + "countCollectedSmall": 186, + "countStolenSmall": 46, + "amountOverfill": 589, + "amountOverfillStolen": 18, + "amountUsedWhileSupersonic": 811, + "timeZeroBoost": 173.11, + "percentZeroBoost": 18.126987, + "timeFullBoost": 62.46, + "percentFullBoost": 6.507438400000001, + "timeBoost0To25": 425.36, + "timeBoost25To50": 193.07999999999998, + "timeBoost50To75": 148.68, + "timeBoost75To100": 173.38, + "percentBoost0To25": 45.34955933333333, + "percentBoost25To50": 20.597202666666664, + "percentBoost50To75": 15.736217333333334, + "percentBoost75To100": 18.317017833333335 + }, + "movement": { + "avgSpeed": 4381, + "totalDistance": 1360577, + "timeSupersonicSpeed": 135.47, + "timeBoostSpeed": 329.42, + "timeSlowSpeed": 497.18999999999994, + "timeGround": 478, + "timeLowAir": 415.09000000000003, + "timeHighAir": 69, + "timePowerslide": 21.67, + "countPowerslide": 209, + "avgPowerslideDuration": 0.31, + "avgSpeedPercentage": 63.492751999999996, + "percentSlowSpeed": 51.71465766666666, + "percentBoostSpeed": 34.234604, + "percentSupersonicSpeed": 14.050738666666668, + "percentGround": 49.66932933333334, + "percentLowAir": 43.149248, + "percentHighAir": 7.181425633333333 + }, + "positioning": { + "avgDistanceToBall": 7709, + "avgDistanceToBallPossession": 7826, + "avgDistanceToBallNoPossession": 7560, + "avgDistanceToMates": 10539, + "timeDefensiveThird": 454.68000000000006, + "timeNeutralThird": 320.08000000000004, + "timeOffensiveThird": 187.33, + "timeDefensiveHalf": 619.82, + "timeOffensiveHalf": 342.27, + "timeBehindBall": 787.9, + "timeInfrontBall": 174.17000000000002, + "timeMostBack": 370.3, + "timeMostForward": 201.39999999999998, + "goalsAgainstWhileLastDefender": 0, + "timeClosestToBall": 343.6, + "timeFarthestFromBall": 265.2, + "percentDefensiveThird": 47.308009000000006, + "percentOffensiveThird": 19.462397499999998, + "percentNeutralThird": 33.229594, + "percentDefensiveHalf": 64.450137, + "percentOffensiveHalf": 35.549866, + "percentBehindBall": 81.93269666666667, + "percentInfrontBall": 18.067300666666668, + "percentMostBack": 38.657314, + "percentMostForward": 21.031329, + "percentClosestToBall": 35.91868566666667, + "percentFarthestFromBall": 27.685582 + }, + "demo": { + "inflicted": 1, + "taken": 6 + } + }, + "advanced": { + "goalParticipation": 40, + "rating": 0.6185064617577237 + } + }, + { + "player": { + "_id": "5faeab91e9ce4ed313ea9570", + "slug": "9570-vatira", + "tag": "Vatira", + "country": "fr" + }, + "stats": { + "core": { + "shots": 3, + "goals": 2, + "saves": 3, + "assists": 0, + "score": 847, + "shootingPercentage": 66.66666666666666 + }, + "boost": { + "bpm": 1274, + "bcpm": 1307.12826, + "avgAmount": 160.11, + "amountCollected": 6947, + "amountStolen": 1431, + "amountCollectedBig": 4633, + "amountStolenBig": 817, + "amountCollectedSmall": 2314, + "amountStolenSmall": 614, + "countCollectedBig": 57, + "countStolenBig": 9, + "countCollectedSmall": 200, + "countStolenSmall": 57, + "amountOverfill": 1156, + "amountOverfillStolen": 104, + "amountUsedWhileSupersonic": 810, + "timeZeroBoost": 109.47999999999999, + "percentZeroBoost": 11.404114666666667, + "timeFullBoost": 137.78, + "percentFullBoost": 14.385866666666667, + "timeBoost0To25": 278.17, + "timeBoost25To50": 195.31, + "timeBoost50To75": 143.23000000000002, + "timeBoost75To100": 350.84000000000003, + "percentBoost0To25": 28.749879333333336, + "percentBoost25To50": 20.172863666666668, + "percentBoost50To75": 14.829629333333331, + "percentBoost75To100": 36.247628 + }, + "movement": { + "avgSpeed": 4842, + "totalDistance": 1530695, + "timeSupersonicSpeed": 201.72, + "timeBoostSpeed": 390, + "timeSlowSpeed": 389.22, + "timeGround": 494.19, + "timeLowAir": 393.23, + "timeHighAir": 93.52, + "timePowerslide": 19.41, + "countPowerslide": 159, + "avgPowerslideDuration": 0.36, + "avgSpeedPercentage": 70.17391533333334, + "percentSlowSpeed": 39.594394666666666, + "percentBoostSpeed": 39.774341666666665, + "percentSupersonicSpeed": 20.631265, + "percentGround": 50.39338833333333, + "percentLowAir": 40.071386, + "percentHighAir": 9.535226 + }, + "positioning": { + "avgDistanceToBall": 8939, + "avgDistanceToBallPossession": 8344, + "avgDistanceToBallNoPossession": 9653, + "avgDistanceToMates": 11144, + "timeDefensiveThird": 411.51, + "timeNeutralThird": 340.03, + "timeOffensiveThird": 229.39000000000001, + "timeDefensiveHalf": 585.36, + "timeOffensiveHalf": 395.58000000000004, + "timeBehindBall": 746.95, + "timeInfrontBall": 234, + "timeMostBack": 342.6, + "timeMostForward": 326.6, + "goalsAgainstWhileLastDefender": 0, + "timeClosestToBall": 296.6, + "timeFarthestFromBall": 381.6, + "percentDefensiveThird": 41.943139333333335, + "percentOffensiveThird": 23.417848000000003, + "percentNeutralThird": 34.639011333333336, + "percentDefensiveHalf": 59.646124333333326, + "percentOffensiveHalf": 40.35387566666666, + "percentBehindBall": 76.09244666666666, + "percentInfrontBall": 23.90755333333333, + "percentMostBack": 35.751522, + "percentMostForward": 34.150982, + "percentClosestToBall": 30.975423666666668, + "percentFarthestFromBall": 39.84656966666666 + }, + "demo": { + "inflicted": 3, + "taken": 0 + } + }, + "advanced": { + "goalParticipation": 40, + "rating": 0.7681727933158508 + } + } + ] + }, + "orange": { + "team": { + "team": { + "_id": "6328af8ec437fde7e02dbd34", + "slug": "bd34-james-cheese", + "name": "James Cheese", + "region": "ASIA", + "relevant": true + }, + "stats": { + "core": { + "shots": 14, + "goals": 0, + "saves": 9, + "assists": 0, + "score": 1920, + "shootingPercentage": 0 + }, + "boost": { + "bpm": 3796, + "bcpm": 3849.3001000000004, + "avgAmount": 429.59000000000003, + "amountCollected": 20476, + "amountStolen": 4764, + "amountCollectedBig": 13548, + "amountStolenBig": 2444, + "amountCollectedSmall": 6928, + "amountStolenSmall": 2320, + "countCollectedBig": 172, + "countStolenBig": 32, + "countCollectedSmall": 610, + "countStolenSmall": 207, + "amountOverfill": 3569, + "amountOverfillStolen": 615, + "amountUsedWhileSupersonic": 3252, + "timeZeroBoost": 341.28999999999996, + "timeFullBoost": 260.25, + "timeBoost0To25": 998.91, + "timeBoost25To50": 747.21, + "timeBoost50To75": 484.72999999999996, + "timeBoost75To100": 655.9300000000001 + }, + "movement": { + "totalDistance": 4545693, + "timeSupersonicSpeed": 554.05, + "timeBoostSpeed": 1210.1400000000003, + "timeSlowSpeed": 1148.37, + "timeGround": 1605.5300000000002, + "timeLowAir": 1104.02, + "timeHighAir": 203.01999999999998, + "timePowerslide": 66.49000000000001, + "countPowerslide": 717 + }, + "positioning": { + "timeDefensiveThird": 1392.29, + "timeNeutralThird": 947.46, + "timeOffensiveThird": 572.8299999999999, + "timeDefensiveHalf": 1900.34, + "timeOffensiveHalf": 1012.24, + "timeBehindBall": 1938.71, + "timeInfrontBall": 973.86 + }, + "demo": { + "inflicted": 10, + "taken": 10 + } + } + }, + "players": [ + { + "player": { + "_id": "5f5ae820c6cbf591c568a3f7", + "slug": "a3f7-virtuoso", + "tag": "virtuoso", + "country": "ie" + }, + "stats": { + "core": { + "shots": 3, + "goals": 0, + "saves": 2, + "assists": 0, + "score": 689, + "shootingPercentage": 0 + }, + "boost": { + "bpm": 1179, + "bcpm": 1218.9927, + "avgAmount": 141.11, + "amountCollected": 6481, + "amountStolen": 1826, + "amountCollectedBig": 4014, + "amountStolenBig": 906, + "amountCollectedSmall": 2467, + "amountStolenSmall": 920, + "countCollectedBig": 55, + "countStolenBig": 13, + "countCollectedSmall": 212, + "countStolenSmall": 83, + "amountOverfill": 1454, + "amountOverfillStolen": 323, + "amountUsedWhileSupersonic": 1264, + "timeZeroBoost": 94.15, + "percentZeroBoost": 9.805358, + "timeFullBoost": 88.95, + "percentFullBoost": 9.305695733333334, + "timeBoost0To25": 340.03, + "timeBoost25To50": 239.06, + "timeBoost50To75": 169.51, + "timeBoost75To100": 218.96, + "percentBoost0To25": 35.06990066666666, + "percentBoost25To50": 24.651270666666665, + "percentBoost50To75": 17.511159333333335, + "percentBoost75To100": 22.767671000000004 + }, + "movement": { + "avgSpeed": 4887, + "totalDistance": 1538882, + "timeSupersonicSpeed": 193.14999999999998, + "timeBoostSpeed": 403.56000000000006, + "timeSlowSpeed": 378.4, + "timeGround": 568.29, + "timeLowAir": 354.46, + "timeHighAir": 52.36, + "timePowerslide": 16.34, + "countPowerslide": 171, + "avgPowerslideDuration": 0.29000000000000004, + "avgSpeedPercentage": 70.82608533333334, + "percentSlowSpeed": 38.78024766666667, + "percentBoostSpeed": 41.39483, + "percentSupersonicSpeed": 19.824921, + "percentGround": 58.30485166666667, + "percentLowAir": 36.328566, + "percentHighAir": 5.366580366666667 + }, + "positioning": { + "avgDistanceToBall": 8466, + "avgDistanceToBallPossession": 7950, + "avgDistanceToBallNoPossession": 8921, + "avgDistanceToMates": 11543, + "timeDefensiveThird": 444.10999999999996, + "timeNeutralThird": 321.6, + "timeOffensiveThird": 209.40999999999997, + "timeDefensiveHalf": 624.0899999999999, + "timeOffensiveHalf": 351.03, + "timeBehindBall": 620.46, + "timeInfrontBall": 354.64, + "timeMostBack": 287.6, + "timeMostForward": 349.6, + "goalsAgainstWhileLastDefender": 1, + "timeClosestToBall": 331.4, + "timeFarthestFromBall": 279.6, + "percentDefensiveThird": 45.55951966666667, + "percentOffensiveThird": 21.515947333333333, + "percentNeutralThird": 32.924531333333334, + "percentDefensiveHalf": 63.97944666666666, + "percentOffensiveHalf": 36.020552, + "percentBehindBall": 63.639134000000006, + "percentInfrontBall": 36.36086366666667, + "percentMostBack": 30.060604333333334, + "percentMostForward": 36.52642, + "percentClosestToBall": 34.633040666666666, + "percentFarthestFromBall": 29.215876666666663 + }, + "demo": { + "inflicted": 5, + "taken": 2 + } + }, + "advanced": { + "goalParticipation": 0, + "rating": 0.20107352063959652 + } + }, + { + "player": { + "_id": "5f3d8fdd95f40596eae23ef5", + "slug": "3ef5-realize", + "tag": "ReaLize", + "country": "jp" + }, + "stats": { + "core": { + "shots": 6, + "goals": 0, + "saves": 2, + "assists": 0, + "score": 513, + "shootingPercentage": 0 + }, + "boost": { + "bpm": 1205, + "bcpm": 1203.11865, + "avgAmount": 140.17000000000002, + "amountCollected": 6403, + "amountStolen": 1425, + "amountCollectedBig": 4447, + "amountStolenBig": 795, + "amountCollectedSmall": 1956, + "amountStolenSmall": 630, + "countCollectedBig": 51, + "countStolenBig": 10, + "countCollectedSmall": 179, + "countStolenSmall": 58, + "amountOverfill": 616, + "amountOverfillStolen": 127, + "amountUsedWhileSupersonic": 665, + "timeZeroBoost": 136.19, + "percentZeroBoost": 14.280424000000002, + "timeFullBoost": 59.6, + "percentFullBoost": 6.219179399999999, + "timeBoost0To25": 360.96000000000004, + "timeBoost25To50": 242.53, + "timeBoost50To75": 149.65, + "timeBoost75To100": 211.26, + "percentBoost0To25": 37.552049000000004, + "percentBoost25To50": 25.092036333333336, + "percentBoost50To75": 15.454405833333333, + "percentBoost75To100": 21.901506666666666 + }, + "movement": { + "avgSpeed": 4665, + "totalDistance": 1470895, + "timeSupersonicSpeed": 155.16, + "timeBoostSpeed": 403.01000000000005, + "timeSlowSpeed": 416.57, + "timeGround": 501.34000000000003, + "timeLowAir": 394.44, + "timeHighAir": 78.97, + "timePowerslide": 23.14, + "countPowerslide": 307, + "avgPowerslideDuration": 0.22000000000000003, + "avgSpeedPercentage": 67.60869466666666, + "percentSlowSpeed": 42.80070966666667, + "percentBoostSpeed": 41.32848333333334, + "percentSupersonicSpeed": 15.870805666666667, + "percentGround": 51.40106533333333, + "percentLowAir": 40.47045166666667, + "percentHighAir": 8.128488566666666 + }, + "positioning": { + "avgDistanceToBall": 9076, + "avgDistanceToBallPossession": 8787, + "avgDistanceToBallNoPossession": 9397, + "avgDistanceToMates": 11613, + "timeDefensiveThird": 493.03999999999996, + "timeNeutralThird": 292.5, + "timeOffensiveThird": 189.20999999999998, + "timeDefensiveHalf": 652.99, + "timeOffensiveHalf": 321.76, + "timeBehindBall": 677.02, + "timeInfrontBall": 297.73, + "timeMostBack": 338.20000000000005, + "timeMostForward": 269.3, + "goalsAgainstWhileLastDefender": 4, + "timeClosestToBall": 320.4, + "timeFarthestFromBall": 320.9, + "percentDefensiveThird": 50.60416633333333, + "percentOffensiveThird": 19.377481666666664, + "percentNeutralThird": 30.01835266666667, + "percentDefensiveHalf": 67.02167333333334, + "percentOffensiveHalf": 32.978322666666664, + "percentBehindBall": 69.505158, + "percentInfrontBall": 30.494844333333333, + "percentMostBack": 35.295197, + "percentMostForward": 28.117872666666667, + "percentClosestToBall": 33.466938999999996, + "percentFarthestFromBall": 33.49135266666667 + }, + "demo": { + "inflicted": 2, + "taken": 2 + } + }, + "advanced": { + "goalParticipation": 0, + "rating": 0.2309988380574517 + } + }, + { + "player": { + "_id": "5f3d8fdd95f40596eae23e36", + "slug": "3e36-kamii", + "tag": "Kamii", + "country": "nz" + }, + "stats": { + "core": { + "shots": 5, + "goals": 0, + "saves": 5, + "assists": 0, + "score": 718, + "shootingPercentage": 0 + }, + "boost": { + "bpm": 1412, + "bcpm": 1427.18875, + "avgAmount": 148.31, + "amountCollected": 7592, + "amountStolen": 1513, + "amountCollectedBig": 5087, + "amountStolenBig": 743, + "amountCollectedSmall": 2505, + "amountStolenSmall": 770, + "countCollectedBig": 66, + "countStolenBig": 9, + "countCollectedSmall": 219, + "countStolenSmall": 66, + "amountOverfill": 1499, + "amountOverfillStolen": 165, + "amountUsedWhileSupersonic": 1323, + "timeZeroBoost": 110.94999999999999, + "percentZeroBoost": 11.578690333333332, + "timeFullBoost": 111.7, + "percentFullBoost": 11.675610999999998, + "timeBoost0To25": 297.91999999999996, + "timeBoost25To50": 265.62, + "timeBoost50To75": 165.57, + "timeBoost75To100": 225.70999999999998, + "percentBoost0To25": 31.236620666666667, + "percentBoost25To50": 27.806632333333337, + "percentBoost50To75": 17.328600333333334, + "percentBoost75To100": 23.628147666666667 + }, + "movement": { + "avgSpeed": 4935, + "totalDistance": 1535916, + "timeSupersonicSpeed": 205.74, + "timeBoostSpeed": 403.57000000000005, + "timeSlowSpeed": 353.4, + "timeGround": 535.9000000000001, + "timeLowAir": 355.12, + "timeHighAir": 71.69, + "timePowerslide": 27.01, + "countPowerslide": 239, + "avgPowerslideDuration": 0.33999999999999997, + "avgSpeedPercentage": 71.52173866666668, + "percentSlowSpeed": 36.69340533333334, + "percentBoostSpeed": 41.942460000000004, + "percentSupersonicSpeed": 21.364132333333334, + "percentGround": 55.71018933333334, + "percentLowAir": 36.850649000000004, + "percentHighAir": 7.439163 + }, + "positioning": { + "avgDistanceToBall": 9410, + "avgDistanceToBallPossession": 8878, + "avgDistanceToBallNoPossession": 9909, + "avgDistanceToMates": 12093, + "timeDefensiveThird": 455.14000000000004, + "timeNeutralThird": 333.36, + "timeOffensiveThird": 174.21, + "timeDefensiveHalf": 623.26, + "timeOffensiveHalf": 339.45, + "timeBehindBall": 641.23, + "timeInfrontBall": 321.49, + "timeMostBack": 354.9, + "timeMostForward": 332.5, + "goalsAgainstWhileLastDefender": 0, + "timeClosestToBall": 302.8, + "timeFarthestFromBall": 374.1, + "percentDefensiveThird": 47.26988466666666, + "percentOffensiveThird": 18.118176666666667, + "percentNeutralThird": 34.611938333333335, + "percentDefensiveHalf": 64.69938, + "percentOffensiveHalf": 35.300619000000005, + "percentBehindBall": 66.61771800000001, + "percentInfrontBall": 33.38228, + "percentMostBack": 37.069853, + "percentMostForward": 34.71951000000001, + "percentClosestToBall": 31.596355000000003, + "percentFarthestFromBall": 39.084326 + }, + "demo": { + "inflicted": 3, + "taken": 6 + } + }, + "advanced": { + "goalParticipation": 0, + "rating": 0.3295264121023839 + } + } + ] + }, + "number": 1, + "games": [ + { + "_id": "6391e88df73a2c40baef6e47", + "blue": 1, + "orange": 0, + "duration": 300, + "ballchasing": "4ac97d74-e868-49f8-91c6-a81bdd519b9a" + }, + { + "_id": "6391eb04f73a2c40baef703a", + "blue": 2, + "orange": 0, + "duration": 300, + "ballchasing": "dd6b4b84-427c-4f5b-9a59-c75399c31282" + }, + { + "_id": "6391ece3f73a2c40baef7420", + "blue": 2, + "orange": 0, + "duration": 300, + "ballchasing": "87bebeb4-ba3e-4650-bd8b-2957e8c2433d" + } + ] +} \ No newline at end of file diff --git a/shared/app/src/commonMain/resources/match_list.json b/shared/app/src/commonMain/resources/match_list.json new file mode 100644 index 000000000..2472ae507 --- /dev/null +++ b/shared/app/src/commonMain/resources/match_list.json @@ -0,0 +1 @@ +{"matches":[{"_id":"63f5ec055a20c5676ad3a6db","slug":"a6db-limitless-vs-mythicx","event":{"_id":"63346f8bda9d7ca1c7bb49cf","slug":"49cf-rlcs-2022-23-winter-sub-saharan-africa-regional-3","name":"RLCS 2022-23 Winter Sub-Saharan Africa Regional 3","region":"AF","mode":3,"tier":"A","image":"https://griffon.octane.gg/events/rlcs-2022-23.png","groups":["rlcs","rlcs2223","rlcs2223winter"]},"stage":{"_id":0,"name":"Group Stage"},"date":"2023-02-24T15:00:00Z","format":{"type":"best","length":5},"blue":{"score":3,"winner":true,"team":{"team":{"_id":"6288982fc437fde7e02d7865","slug":"7865-limitless","name":"Limitless","region":"AF","image":"https://griffon.octane.gg/teams/Limitless.png","relevant":true},"stats":{"core":{"shots":33,"goals":13,"saves":13,"assists":11,"score":4190,"shootingPercentage":39.39393939393939},"boost":{"bpm":3829,"bcpm":4077.7790299999997,"avgAmount":440.79,"amountCollected":23172,"amountStolen":5473,"amountCollectedBig":17028,"amountStolenBig":3295,"amountCollectedSmall":6144,"amountStolenSmall":2178,"countCollectedBig":205,"countStolenBig":42,"countCollectedSmall":532,"countStolenSmall":178,"amountOverfill":3707,"amountOverfillStolen":925,"amountUsedWhileSupersonic":2824,"timeZeroBoost":323.31000000000006,"timeFullBoost":448.26,"timeBoost0To25":1057.81,"timeBoost25To50":627.6,"timeBoost50To75":604.9000000000001,"timeBoost75To100":866.85},"movement":{"totalDistance":4616604,"timeSupersonicSpeed":450.84000000000003,"timeBoostSpeed":1279.81,"timeSlowSpeed":1491.55,"timeGround":1819.9,"timeLowAir":1213.1399999999999,"timeHighAir":189.17000000000002,"timePowerslide":60.22999999999999,"countPowerslide":477},"positioning":{"timeDefensiveThird":1433.44,"timeNeutralThird":1068.33,"timeOffensiveThird":720.41,"timeDefensiveHalf":2011.48,"timeOffensiveHalf":1210.73,"timeBehindBall":2352.37,"timeInfrontBall":869.84},"demo":{"inflicted":12,"taken":6}}},"players":[{"player":{"_id":"60bb4abf6d9c1362119ca7d4","slug":"a7d4-snowyy","tag":"Snowyy","country":"za"},"stats":{"core":{"shots":10,"goals":3,"saves":1,"assists":3,"score":992,"shootingPercentage":30},"boost":{"bpm":1533,"bcpm":1637.99242,"avgAmount":144.98000000000002,"amountCollected":9303,"amountStolen":2140,"amountCollectedBig":7082,"amountStolenBig":1358,"amountCollectedSmall":2221,"amountStolenSmall":782,"countCollectedBig":80,"countStolenBig":17,"countCollectedSmall":200,"countStolenSmall":71,"amountOverfill":998,"amountOverfillStolen":353,"amountUsedWhileSupersonic":1346,"timeZeroBoost":109.39,"percentZeroBoost":10.699546333333332,"timeFullBoost":110.06,"percentFullBoost":10.735605000000001,"timeBoost0To25":424.95000000000005,"timeBoost25To50":193.17,"timeBoost50To75":205.67000000000002,"timeBoost75To100":233.14,"percentBoost0To25":40.148150333333334,"percentBoost25To50":18.284803999999998,"percentBoost50To75":19.496523,"percentBoost75To100":22.070524666666667},"movement":{"avgSpeed":4875,"totalDistance":1627273,"timeSupersonicSpeed":196.49,"timeBoostSpeed":453.44000000000005,"timeSlowSpeed":425.35,"timeGround":587.4200000000001,"timeLowAir":427.43999999999994,"timeHighAir":60.410000000000004,"timePowerslide":23.509999999999998,"countPowerslide":198,"avgPowerslideDuration":0.36,"avgSpeedPercentage":70.65217666666668,"percentSlowSpeed":39.49477066666666,"percentBoostSpeed":42.252018666666665,"percentSupersonicSpeed":18.253211333333333,"percentGround":54.65681333333333,"percentLowAir":39.768488,"percentHighAir":5.5746937},"positioning":{"avgDistanceToBall":8209,"avgDistanceToBallPossession":8007,"avgDistanceToBallNoPossession":8592,"avgDistanceToMates":10840,"timeDefensiveThird":461.26,"timeNeutralThird":366.61,"timeOffensiveThird":247.39999999999998,"timeDefensiveHalf":654.9100000000001,"timeOffensiveHalf":420.36999999999995,"timeBehindBall":769.37,"timeInfrontBall":305.9,"timeMostBack":310.7,"timeMostForward":353.29999999999995,"goalsAgainstWhileLastDefender":1,"timeClosestToBall":362.79999999999995,"timeFarthestFromBall":346.2,"percentDefensiveThird":42.86692733333334,"percentOffensiveThird":23.049705,"percentNeutralThird":34.083367,"percentDefensiveHalf":60.86353033333333,"percentOffensiveHalf":39.136471,"percentBehindBall":71.56261666666667,"percentInfrontBall":28.437383,"percentMostBack":30.413766999999996,"percentMostForward":34.55847933333333,"percentClosestToBall":35.477204666666665,"percentFarthestFromBall":33.807616333333335},"demo":{"inflicted":6,"taken":2}},"advanced":{"goalParticipation":46.15384615384615,"rating":1.134562114343825}},{"player":{"_id":"60bb4abf6d9c1362119ca7d3","slug":"a7d3-darth","tag":"Darth.","country":"za"},"stats":{"core":{"shots":11,"goals":6,"saves":3,"assists":3,"score":1444,"shootingPercentage":54.54545454545454},"boost":{"bpm":1068,"bcpm":1165.57632,"avgAmount":156.86,"amountCollected":6630,"amountStolen":1540,"amountCollectedBig":4629,"amountStolenBig":927,"amountCollectedSmall":2001,"amountStolenSmall":613,"countCollectedBig":60,"countStolenBig":11,"countCollectedSmall":172,"countStolenSmall":47,"amountOverfill":1471,"amountOverfillStolen":181,"amountUsedWhileSupersonic":304,"timeZeroBoost":100.52000000000001,"percentZeroBoost":9.7765249,"timeFullBoost":183.85,"percentFullBoost":18.111140333333335,"timeBoost0To25":274.53,"timeBoost25To50":212.43,"timeBoost50To75":203.48000000000002,"timeBoost75To100":353.57,"percentBoost0To25":26.273171,"percentBoost25To50":20.33314366666667,"percentBoost50To75":19.493857666666667,"percentBoost75To100":33.89982933333334},"movement":{"avgSpeed":4318,"totalDistance":1427010,"timeSupersonicSpeed":100.88,"timeBoostSpeed":392.07,"timeSlowSpeed":575.5999999999999,"timeGround":622.41,"timeLowAir":388.4,"timeHighAir":57.75,"timePowerslide":21.01,"countPowerslide":155,"avgPowerslideDuration":0.41000000000000003,"avgSpeedPercentage":62.57971,"percentSlowSpeed":53.847294,"percentBoostSpeed":36.707766,"percentSupersonicSpeed":9.444937000000001,"percentGround":58.23883433333333,"percentLowAir":36.357290666666664,"percentHighAir":5.4038736},"positioning":{"avgDistanceToBall":8855,"avgDistanceToBallPossession":8721,"avgDistanceToBallNoPossession":8985,"avgDistanceToMates":10995,"timeDefensiveThird":508.1600000000001,"timeNeutralThird":353.44,"timeOffensiveThird":206.94,"timeDefensiveHalf":701.1099999999999,"timeOffensiveHalf":367.45,"timeBehindBall":811.3499999999999,"timeInfrontBall":257.21000000000004,"timeMostBack":412.2,"timeMostForward":279.70000000000005,"goalsAgainstWhileLastDefender":1,"timeClosestToBall":280.3,"timeFarthestFromBall":376.90000000000003,"percentDefensiveThird":47.548965333333335,"percentOffensiveThird":19.371649333333334,"percentNeutralThird":33.07938,"percentDefensiveHalf":65.61736133333333,"percentOffensiveHalf":34.38263633333333,"percentBehindBall":75.92307999999998,"percentInfrontBall":24.07692133333333,"percentMostBack":40.259097,"percentMostForward":27.337902,"percentClosestToBall":27.41266,"percentFarthestFromBall":36.78702366666667},"demo":{"inflicted":1,"taken":3}},"advanced":{"goalParticipation":69.23076923076923,"rating":1.6974178738610035}},{"player":{"_id":"60c46a3a9fc1a47e5f11199a","slug":"199a-2die4","tag":"2Die4","country":"za"},"stats":{"core":{"shots":12,"goals":4,"saves":9,"assists":5,"score":1754,"shootingPercentage":33.33333333333333},"boost":{"bpm":1228,"bcpm":1274.21029,"avgAmount":138.95,"amountCollected":7239,"amountStolen":1793,"amountCollectedBig":5317,"amountStolenBig":1010,"amountCollectedSmall":1922,"amountStolenSmall":783,"countCollectedBig":65,"countStolenBig":14,"countCollectedSmall":160,"countStolenSmall":60,"amountOverfill":1238,"amountOverfillStolen":391,"amountUsedWhileSupersonic":1174,"timeZeroBoost":113.4,"percentZeroBoost":11.140929,"timeFullBoost":154.35,"percentFullBoost":15.10553,"timeBoost0To25":358.33000000000004,"timeBoost25To50":222,"timeBoost50To75":195.75,"timeBoost75To100":280.14,"percentBoost0To25":33.94128799999999,"percentBoost25To50":21.065520333333332,"percentBoost50To75":18.572419666666665,"percentBoost75To100":26.42077},"movement":{"avgSpeed":4682,"totalDistance":1562321,"timeSupersonicSpeed":153.47000000000003,"timeBoostSpeed":434.29999999999995,"timeSlowSpeed":490.6,"timeGround":610.07,"timeLowAir":397.29999999999995,"timeHighAir":71.01,"timePowerslide":15.709999999999997,"countPowerslide":124,"avgPowerslideDuration":0.38,"avgSpeedPercentage":67.85507466666665,"percentSlowSpeed":45.4484,"percentBoostSpeed":40.314493,"percentSupersonicSpeed":14.237112333333334,"percentGround":56.55997466666667,"percentLowAir":36.864287,"percentHighAir":6.575737333333333},"positioning":{"avgDistanceToBall":8068,"avgDistanceToBallPossession":7762,"avgDistanceToBallNoPossession":8455,"avgDistanceToMates":10843,"timeDefensiveThird":464.02,"timeNeutralThird":348.28000000000003,"timeOffensiveThird":266.07,"timeDefensiveHalf":655.46,"timeOffensiveHalf":422.9100000000001,"timeBehindBall":771.65,"timeInfrontBall":306.73,"timeMostBack":302.20000000000005,"timeMostForward":378,"goalsAgainstWhileLastDefender":2,"timeClosestToBall":367.8,"timeFarthestFromBall":302,"percentDefensiveThird":43.010278666666665,"percentOffensiveThird":24.69062,"percentNeutralThird":32.299103333333335,"percentDefensiveHalf":60.744565333333334,"percentOffensiveHalf":39.255433333333336,"percentBehindBall":71.49306,"percentInfrontBall":28.50693766666667,"percentMostBack":29.508053666666665,"percentMostForward":36.91223266666667,"percentClosestToBall":35.91061133333333,"percentFarthestFromBall":29.585885666666666},"demo":{"inflicted":5,"taken":1}},"advanced":{"goalParticipation":69.23076923076923,"rating":1.8420954446794262}}]},"orange":{"team":{"team":{"_id":"63e4f1b5f73a2c40bafd0c67","slug":"0c67-mythicx","name":"Mythicx","region":"AF","image":"https://griffon.octane.gg/teams/Mythicx.png"},"stats":{"core":{"shots":21,"goals":4,"saves":10,"assists":4,"score":2439,"shootingPercentage":19.047619047619047},"boost":{"bpm":3162,"bcpm":3244.82789,"avgAmount":413.82,"amountCollected":18443,"amountStolen":3925,"amountCollectedBig":12096,"amountStolenBig":2011,"amountCollectedSmall":6347,"amountStolenSmall":1914,"countCollectedBig":135,"countStolenBig":22,"countCollectedSmall":549,"countStolenSmall":156,"amountOverfill":1583,"amountOverfillStolen":209,"amountUsedWhileSupersonic":1747,"timeZeroBoost":435.02,"timeFullBoost":289.48,"timeBoost0To25":1273.6699999999998,"timeBoost25To50":696.25,"timeBoost50To75":440.84000000000003,"timeBoost75To100":697.03},"movement":{"totalDistance":4462566,"timeSupersonicSpeed":362.32,"timeBoostSpeed":1252.5,"timeSlowSpeed":1589.96,"timeGround":1780.6599999999999,"timeLowAir":1265.1,"timeHighAir":159.01,"timePowerslide":49.98,"countPowerslide":558},"positioning":{"timeDefensiveThird":1557.81,"timeNeutralThird":1023.29,"timeOffensiveThird":623.6800000000001,"timeDefensiveHalf":2112.43,"timeOffensiveHalf":1092.35,"timeBehindBall":2138.8599999999997,"timeInfrontBall":1065.93},"demo":{"inflicted":6,"taken":12}}},"players":[{"player":{"_id":"621ca419c437fde7e02d0f55","slug":"0f55-amogus","tag":"amogus","country":"za"},"stats":{"core":{"shots":8,"goals":1,"saves":4,"assists":2,"score":764,"shootingPercentage":12.5},"boost":{"bpm":829,"bcpm":854.70856,"avgAmount":131.2,"amountCollected":4867,"amountStolen":983,"amountCollectedBig":2759,"amountStolenBig":390,"amountCollectedSmall":2108,"amountStolenSmall":593,"countCollectedBig":32,"countStolenBig":4,"countCollectedSmall":185,"countStolenSmall":51,"amountOverfill":481,"amountOverfillStolen":15,"amountUsedWhileSupersonic":247,"timeZeroBoost":65.36999999999999,"percentZeroBoost":6.414277133333333,"timeFullBoost":92.46,"percentFullBoost":9.028009833333334,"timeBoost0To25":435.9,"timeBoost25To50":281.21000000000004,"timeBoost50To75":136.01,"timeBoost75To100":194.09,"percentBoost0To25":41.624358666666666,"percentBoost25To50":26.912674999999997,"percentBoost50To75":12.9990705,"percentBoost75To100":18.463896000000002},"movement":{"avgSpeed":4392,"totalDistance":1465821,"timeSupersonicSpeed":86.8,"timeBoostSpeed":415.76,"timeSlowSpeed":575.77,"timeGround":585.49,"timeLowAir":452.78000000000003,"timeHighAir":40.07,"timePowerslide":8.85,"countPowerslide":81,"avgPowerslideDuration":0.32,"avgSpeedPercentage":63.65217466666667,"percentSlowSpeed":53.44340400000001,"percentBoostSpeed":38.511233,"percentSupersonicSpeed":8.045364,"percentGround":54.32018733333334,"percentLowAir":41.98829666666666,"percentHighAir":3.691516033333334},"positioning":{"avgDistanceToBall":8053,"avgDistanceToBallPossession":8125,"avgDistanceToBallNoPossession":8089,"avgDistanceToMates":10441,"timeDefensiveThird":526.02,"timeNeutralThird":346.93,"timeOffensiveThird":205.39,"timeDefensiveHalf":717.36,"timeOffensiveHalf":360.98,"timeBehindBall":734.1899999999999,"timeInfrontBall":344.15000000000003,"timeMostBack":336.6,"timeMostForward":344,"goalsAgainstWhileLastDefender":5,"timeClosestToBall":334.4,"timeFarthestFromBall":322.9,"percentDefensiveThird":48.86779533333333,"percentOffensiveThird":18.988638333333334,"percentNeutralThird":32.143567999999995,"percentDefensiveHalf":66.59034333333334,"percentOffensiveHalf":33.409657333333335,"percentBehindBall":68.127054,"percentInfrontBall":31.872945666666666,"percentMostBack":32.95210633333334,"percentMostForward":33.55097966666667,"percentClosestToBall":32.65869,"percentFarthestFromBall":31.518471},"demo":{"inflicted":2,"taken":1}},"advanced":{"goalParticipation":75,"rating":0.8197906540755304}},{"player":{"_id":"61f91694da9d7ca1c7ba759a","slug":"759a-duklaz","tag":"Duklaz","country":"za"},"stats":{"core":{"shots":6,"goals":2,"saves":4,"assists":1,"score":962,"shootingPercentage":33.33333333333333},"boost":{"bpm":1165,"bcpm":1166.33286,"avgAmount":124.09,"amountCollected":6617,"amountStolen":1369,"amountCollectedBig":4181,"amountStolenBig":485,"amountCollectedSmall":2436,"amountStolenSmall":884,"countCollectedBig":47,"countStolenBig":5,"countCollectedSmall":203,"countStolenSmall":66,"amountOverfill":580,"amountOverfillStolen":26,"amountUsedWhileSupersonic":950,"timeZeroBoost":243.89,"percentZeroBoost":23.817303,"timeFullBoost":66.66,"percentFullBoost":6.538878933333334,"timeBoost0To25":530.5699999999999,"timeBoost25To50":196.3,"timeBoost50To75":133.72,"timeBoost75To100":171.38,"percentBoost0To25":51.334087333333336,"percentBoost25To50":18.990226333333336,"percentBoost50To75":13.062209333333334,"percentBoost75To100":16.613478},"movement":{"avgSpeed":4784,"totalDistance":1567677,"timeSupersonicSpeed":150.47,"timeBoostSpeed":467.32000000000005,"timeSlowSpeed":440.79999999999995,"timeGround":589.84,"timeLowAir":411.15000000000003,"timeHighAir":57.59,"timePowerslide":19.15,"countPowerslide":233,"avgPowerslideDuration":0.25,"avgSpeedPercentage":69.33333266666666,"percentSlowSpeed":41.51462266666667,"percentBoostSpeed":44.26822033333334,"percentSupersonicSpeed":14.217163,"percentGround":55.71977233333333,"percentLowAir":38.850839,"percentHighAir":5.429388933333333},"positioning":{"avgDistanceToBall":7939,"avgDistanceToBallPossession":7692,"avgDistanceToBallNoPossession":8183,"avgDistanceToMates":10484,"timeDefensiveThird":518.76,"timeNeutralThird":319.86,"timeOffensiveThird":219.96,"timeDefensiveHalf":694.3199999999999,"timeOffensiveHalf":364.27,"timeBehindBall":685.67,"timeInfrontBall":372.92,"timeMostBack":327.5,"timeMostForward":332,"goalsAgainstWhileLastDefender":5,"timeClosestToBall":364.2,"timeFarthestFromBall":339.5,"percentDefensiveThird":48.989951999999995,"percentOffensiveThird":20.795256666666667,"percentNeutralThird":30.21479533333333,"percentDefensiveHalf":65.56789666666667,"percentOffensiveHalf":34.43210666666667,"percentBehindBall":64.70978266666667,"percentInfrontBall":35.290216,"percentMostBack":31.971632333333332,"percentMostForward":32.500464666666666,"percentClosestToBall":35.644128,"percentFarthestFromBall":33.17612666666667},"demo":{"inflicted":2,"taken":6}},"advanced":{"goalParticipation":75,"rating":0.8892184297218929}},{"player":{"_id":"60c46906490bd8649dd30e5b","slug":"0e5b-jeff","tag":"Jeff","country":"za"},"stats":{"core":{"shots":7,"goals":1,"saves":2,"assists":1,"score":713,"shootingPercentage":14.285714285714285},"boost":{"bpm":1168,"bcpm":1223.78647,"avgAmount":158.53,"amountCollected":6959,"amountStolen":1573,"amountCollectedBig":5156,"amountStolenBig":1136,"amountCollectedSmall":1803,"amountStolenSmall":437,"countCollectedBig":56,"countStolenBig":13,"countCollectedSmall":161,"countStolenSmall":39,"amountOverfill":522,"amountOverfillStolen":168,"amountUsedWhileSupersonic":550,"timeZeroBoost":125.76,"percentZeroBoost":12.304151333333332,"timeFullBoost":130.35999999999999,"percentFullBoost":12.703924,"timeBoost0To25":307.2,"timeBoost25To50":218.74,"timeBoost50To75":171.11,"timeBoost75To100":331.56,"percentBoost0To25":30.060333,"percentBoost25To50":21.22939,"percentBoost50To75":16.764161,"percentBoost75To100":31.946117},"movement":{"avgSpeed":4321,"totalDistance":1429068,"timeSupersonicSpeed":125.05,"timeBoostSpeed":369.42,"timeSlowSpeed":573.39,"timeGround":605.3299999999999,"timeLowAir":401.16999999999996,"timeHighAir":61.349999999999994,"timePowerslide":21.979999999999997,"countPowerslide":244,"avgPowerslideDuration":0.27,"avgSpeedPercentage":62.623188,"percentSlowSpeed":53.75730433333333,"percentBoostSpeed":34.55787766666666,"percentSupersonicSpeed":11.684821666666666,"percentGround":56.69740266666667,"percentLowAir":37.538951,"percentHighAir":5.763654133333333},"positioning":{"avgDistanceToBall":8625,"avgDistanceToBallPossession":8757,"avgDistanceToBallNoPossession":8526,"avgDistanceToMates":10612,"timeDefensiveThird":513.03,"timeNeutralThird":356.5,"timeOffensiveThird":198.32999999999998,"timeDefensiveHalf":700.75,"timeOffensiveHalf":367.1,"timeBehindBall":719,"timeInfrontBall":348.86,"timeMostBack":376.6,"timeMostForward":336.7,"goalsAgainstWhileLastDefender":3,"timeClosestToBall":312,"timeFarthestFromBall":377.8,"percentDefensiveThird":48.112480000000005,"percentOffensiveThird":18.538700333333335,"percentNeutralThird":33.348817,"percentDefensiveHalf":65.68931533333334,"percentOffensiveHalf":34.310687333333334,"percentBehindBall":67.32130033333333,"percentInfrontBall":32.678702,"percentMostBack":36.807161666666666,"percentMostForward":32.928777000000004,"percentClosestToBall":30.46523666666667,"percentFarthestFromBall":37.003143333333334},"demo":{"inflicted":2,"taken":5}},"advanced":{"goalParticipation":50,"rating":0.6940667344706356}}]},"number":1,"games":[{"_id":"63f8d3bcf73a2c40ba04201d","blue":4,"orange":1,"duration":300,"ballchasing":"47e85aaa-aa4c-44c4-88fd-eff816680d8c"},{"_id":"63f8d5d95a20c5676ad50848","blue":5,"orange":2,"duration":300,"ballchasing":"1ff4dc46-e297-45ab-a2ee-ecc0ed1e9942"},{"_id":"63f8d786f73a2c40ba0431a8","blue":4,"orange":1,"duration":300,"ballchasing":"9d64fb1c-5d81-459b-a4ab-2097d9bd4713"}]},{"_id":"63f5ec055a20c5676ad3a6dc","slug":"a6dc-srz-vs-astronic-esports","event":{"_id":"63346f8bda9d7ca1c7bb49cf","slug":"49cf-rlcs-2022-23-winter-sub-saharan-africa-regional-3","name":"RLCS 2022-23 Winter Sub-Saharan Africa Regional 3","region":"AF","mode":3,"tier":"A","image":"https://griffon.octane.gg/events/rlcs-2022-23.png","groups":["rlcs","rlcs2223","rlcs2223winter"]},"stage":{"_id":0,"name":"Group Stage"},"date":"2023-02-24T15:00:00Z","format":{"type":"best","length":5},"blue":{"score":3,"winner":true,"team":{"team":{"_id":"63d159445a20c5676ac5e019","slug":"e019-srz","name":"SRZ","region":"AF","relevant":true},"stats":{"core":{"shots":47,"goals":12,"saves":17,"assists":8,"score":5101,"shootingPercentage":25.53191489361702},"boost":{"bpm":6197,"bcpm":6535.45447,"avgAmount":790.19,"amountCollected":38030,"amountStolen":8315,"amountCollectedBig":25212,"amountStolenBig":4083,"amountCollectedSmall":12818,"amountStolenSmall":4232,"countCollectedBig":309,"countStolenBig":50,"countCollectedSmall":1177,"countStolenSmall":382,"amountOverfill":6350,"amountOverfillStolen":998,"amountUsedWhileSupersonic":3892,"timeZeroBoost":487.09,"timeFullBoost":629.25,"timeBoost0To25":1612.96,"timeBoost25To50":1146.51,"timeBoost50To75":1000.6800000000001,"timeBoost75To100":1570.99},"movement":{"totalDistance":8127314,"timeSupersonicSpeed":871.0899999999999,"timeBoostSpeed":2341.5099999999998,"timeSlowSpeed":2201.29,"timeGround":3172.4500000000003,"timeLowAir":1953.0800000000002,"timeHighAir":288.37,"timePowerslide":103.69,"countPowerslide":1039},"positioning":{"timeDefensiveThird":2433.83,"timeNeutralThird":1788.47,"timeOffensiveThird":1191.58,"timeDefensiveHalf":3367.0199999999995,"timeOffensiveHalf":2046.88,"timeBehindBall":3917.38,"timeInfrontBall":1496.4900000000002},"demo":{"inflicted":33,"taken":14}}},"players":[{"player":{"_id":"60c46a74490bd8649dd30e79","slug":"0e79-ram","tag":"Ram","country":"za"},"stats":{"core":{"shots":18,"goals":4,"saves":6,"assists":4,"score":1797,"shootingPercentage":22.22222222222222},"boost":{"bpm":2212,"bcpm":2297.34544,"avgAmount":249.70000000000002,"amountCollected":13362,"amountStolen":2920,"amountCollectedBig":9112,"amountStolenBig":1647,"amountCollectedSmall":4250,"amountStolenSmall":1273,"countCollectedBig":109,"countStolenBig":20,"countCollectedSmall":388,"countStolenSmall":115,"amountOverfill":2008,"amountOverfillStolen":368,"amountUsedWhileSupersonic":1374,"timeZeroBoost":207.48000000000002,"percentZeroBoost":12.08415866,"timeFullBoost":174.66000000000003,"percentFullBoost":9.972536,"timeBoost0To25":647.3299999999999,"timeBoost25To50":364.42999999999995,"timeBoost50To75":265.90999999999997,"timeBoost75To100":510.33,"percentBoost0To25":36.4463034,"percentBoost25To50":20.2436832,"percentBoost50To75":14.8656747,"percentBoost75To100":28.444339199999995},"movement":{"avgSpeed":8058,"totalDistance":2756311,"timeSupersonicSpeed":317.38,"timeBoostSpeed":789.06,"timeSlowSpeed":705.1099999999999,"timeGround":1077.99,"timeLowAir":638.57,"timeHighAir":95.00999999999999,"timePowerslide":38.79,"countPowerslide":409,"avgPowerslideDuration":0.47000000000000003,"avgSpeedPercentage":70.06956439999999,"percentSlowSpeed":38.964131800000004,"percentBoostSpeed":43.5480054,"percentSupersonicSpeed":17.4878654,"percentGround":59.3400298,"percentLowAir":35.354452200000004,"percentHighAir":5.30551626},"positioning":{"avgDistanceToBall":15080,"avgDistanceToBallPossession":14805,"avgDistanceToBallNoPossession":15380,"avgDistanceToMates":19137,"timeDefensiveThird":838.64,"timeNeutralThird":579.3599999999999,"timeOffensiveThird":393.54999999999995,"timeDefensiveHalf":1152.23,"timeOffensiveHalf":659.33,"timeBehindBall":1338.86,"timeInfrontBall":472.7,"timeMostBack":613.5,"timeMostForward":571.6,"goalsAgainstWhileLastDefender":3,"timeClosestToBall":570.5999999999999,"timeFarthestFromBall":577.4,"percentDefensiveThird":46.48289,"percentOffensiveThird":21.5170956,"percentNeutralThird":32.0000134,"percentDefensiveHalf":63.799258800000004,"percentOffensiveHalf":36.2007452,"percentBehindBall":73.9378732,"percentInfrontBall":26.062125599999995,"percentMostBack":35.2711724,"percentMostForward":32.888857200000004,"percentClosestToBall":32.88471439999999,"percentFarthestFromBall":33.2643936},"demo":{"inflicted":14,"taken":2}},"advanced":{"goalParticipation":66.66666666666666,"rating":1.1381868140508384}},{"player":{"_id":"61731698143c37878b23a133","slug":"a133-sweaty","tag":"Sweaty","country":"za"},"stats":{"core":{"shots":17,"goals":4,"saves":6,"assists":2,"score":1657,"shootingPercentage":23.52941176470588},"boost":{"bpm":1935,"bcpm":2064.9991999999997,"avgAmount":264.55,"amountCollected":12044,"amountStolen":2332,"amountCollectedBig":7360,"amountStolenBig":787,"amountCollectedSmall":4684,"amountStolenSmall":1545,"countCollectedBig":93,"countStolenBig":10,"countCollectedSmall":425,"countStolenSmall":138,"amountOverfill":2129,"amountOverfillStolen":240,"amountUsedWhileSupersonic":907,"timeZeroBoost":120.28,"percentZeroBoost":6.898866979999999,"timeFullBoost":177.85,"percentFullBoost":10.25883352,"timeBoost0To25":479.36,"timeBoost25To50":430.57,"timeBoost50To75":376.37,"timeBoost75To100":479.89,"percentBoost0To25":26.7344838,"percentBoost25To50":24.4105106,"percentBoost50To75":21.4336424,"percentBoost75To100":27.421363200000002},"movement":{"avgSpeed":7794,"totalDistance":2631109,"timeSupersonicSpeed":242.01,"timeBoostSpeed":801.6299999999999,"timeSlowSpeed":747.98,"timeGround":1079.02,"timeLowAir":632.77,"timeHighAir":79.83,"timePowerslide":37.32,"countPowerslide":367,"avgPowerslideDuration":0.51,"avgSpeedPercentage":67.77391279999999,"percentSlowSpeed":41.6881348,"percentBoostSpeed":44.8882882,"percentSupersonicSpeed":13.4235782,"percentGround":60.380708199999994,"percentLowAir":35.149754599999994,"percentHighAir":4.46953774},"positioning":{"avgDistanceToBall":14246,"avgDistanceToBallPossession":13169,"avgDistanceToBallNoPossession":15288,"avgDistanceToMates":18446,"timeDefensiveThird":806.82,"timeNeutralThird":604.37,"timeOffensiveThird":380.41999999999996,"timeDefensiveHalf":1121.1799999999998,"timeOffensiveHalf":670.44,"timeBehindBall":1337.2100000000003,"timeInfrontBall":454.40000000000003,"timeMostBack":597.6,"timeMostForward":514.5,"goalsAgainstWhileLastDefender":5,"timeClosestToBall":590.7,"timeFarthestFromBall":558.1999999999999,"percentDefensiveThird":45.205273,"percentOffensiveThird":21.1246868,"percentNeutralThird":33.6700386,"percentDefensiveHalf":62.746795,"percentOffensiveHalf":37.2532042,"percentBehindBall":74.70318599999999,"percentInfrontBall":25.296811400000003,"percentMostBack":34.3968948,"percentMostForward":29.3772376,"percentClosestToBall":33.8241706,"percentFarthestFromBall":32.0502416},"demo":{"inflicted":10,"taken":9}},"advanced":{"goalParticipation":50,"rating":0.9248378947885261}},{"player":{"_id":"60d08e2288116f536df975a1","slug":"75a1-zero","tag":"Zero","country":"za"},"stats":{"core":{"shots":12,"goals":4,"saves":5,"assists":2,"score":1647,"shootingPercentage":33.33333333333333},"boost":{"bpm":2050,"bcpm":2173.1098300000003,"avgAmount":275.94,"amountCollected":12624,"amountStolen":3063,"amountCollectedBig":8740,"amountStolenBig":1649,"amountCollectedSmall":3884,"amountStolenSmall":1414,"countCollectedBig":107,"countStolenBig":20,"countCollectedSmall":364,"countStolenSmall":129,"amountOverfill":2213,"amountOverfillStolen":390,"amountUsedWhileSupersonic":1611,"timeZeroBoost":159.32999999999998,"percentZeroBoost":9.343646000000001,"timeFullBoost":276.74,"percentFullBoost":16.038133600000002,"timeBoost0To25":486.27,"timeBoost25To50":351.51000000000005,"timeBoost50To75":358.40000000000003,"timeBoost75To100":580.77,"percentBoost0To25":27.2335334,"percentBoost25To50":19.845194,"percentBoost50To75":20.123758400000003,"percentBoost75To100":32.797517},"movement":{"avgSpeed":8033,"totalDistance":2739894,"timeSupersonicSpeed":311.7,"timeBoostSpeed":750.8199999999999,"timeSlowSpeed":748.1999999999999,"timeGround":1015.44,"timeLowAir":681.74,"timeHighAir":113.53,"timePowerslide":27.58,"countPowerslide":263,"avgPowerslideDuration":0.53,"avgSpeedPercentage":69.8521752,"percentSlowSpeed":41.382476,"percentBoostSpeed":41.5110232,"percentSupersonicSpeed":17.106500399999998,"percentGround":56.1103424,"percentLowAir":37.623942400000004,"percentHighAir":6.26571412},"positioning":{"avgDistanceToBall":15063,"avgDistanceToBallPossession":14178,"avgDistanceToBallNoPossession":15983,"avgDistanceToMates":19217,"timeDefensiveThird":788.37,"timeNeutralThird":604.74,"timeOffensiveThird":417.60999999999996,"timeDefensiveHalf":1093.61,"timeOffensiveHalf":717.11,"timeBehindBall":1241.31,"timeInfrontBall":569.3900000000001,"timeMostBack":563.4,"timeMostForward":656.5999999999999,"goalsAgainstWhileLastDefender":3,"timeClosestToBall":584.8,"timeFarthestFromBall":631.3,"percentDefensiveThird":43.6047074,"percentOffensiveThird":23.035695,"percentNeutralThird":33.3595974,"percentDefensiveHalf":60.4927264,"percentOffensiveHalf":39.5072736,"percentBehindBall":68.4733264,"percentInfrontBall":31.526671399999998,"percentMostBack":32.404135,"percentMostForward":37.9386344,"percentClosestToBall":33.6989866,"percentFarthestFromBall":36.311554},"demo":{"inflicted":9,"taken":3}},"advanced":{"goalParticipation":50,"rating":0.9511557966068065}}]},"orange":{"score":2,"team":{"team":{"_id":"6266b288c437fde7e02d4572","slug":"4572-astronic-esports","name":"Astronic Esports","region":"AF","image":"https://griffon.octane.gg/teams/Astronic_Esports.png"},"stats":{"core":{"shots":32,"goals":11,"saves":28,"assists":9,"score":5798,"shootingPercentage":34.375},"boost":{"bpm":5914,"bcpm":6002.17892,"avgAmount":716.2,"amountCollected":34698,"amountStolen":6429,"amountCollectedBig":24029,"amountStolenBig":3607,"amountCollectedSmall":10669,"amountStolenSmall":2822,"countCollectedBig":287,"countStolenBig":41,"countCollectedSmall":936,"countStolenSmall":243,"amountOverfill":4723,"amountOverfillStolen":538,"amountUsedWhileSupersonic":3954,"timeZeroBoost":539.49,"timeFullBoost":467.57,"timeBoost0To25":1879.0099999999998,"timeBoost25To50":1252.8400000000001,"timeBoost50To75":969.6800000000001,"timeBoost75To100":1105.99},"movement":{"totalDistance":7740308,"timeSupersonicSpeed":815.1700000000001,"timeBoostSpeed":2049.23,"timeSlowSpeed":2499.56,"timeGround":3060.1100000000006,"timeLowAir":2040.4699999999998,"timeHighAir":263.39,"timePowerslide":95.09,"countPowerslide":946},"positioning":{"timeDefensiveThird":2722.75,"timeNeutralThird":1645.2599999999998,"timeOffensiveThird":995.95,"timeDefensiveHalf":3628.3499999999995,"timeOffensiveHalf":1735.6299999999999,"timeBehindBall":3878.85,"timeInfrontBall":1485.12},"demo":{"inflicted":14,"taken":33}}},"players":[{"player":{"_id":"61801153f8090ec74528a649","slug":"a649-strutic","tag":"strutic","country":"hu"},"stats":{"core":{"shots":15,"goals":6,"saves":8,"assists":2,"score":2237,"shootingPercentage":40},"boost":{"bpm":2097,"bcpm":2097.24073,"avgAmount":240.07,"amountCollected":12171,"amountStolen":1752,"amountCollectedBig":8365,"amountStolenBig":973,"amountCollectedSmall":3806,"amountStolenSmall":779,"countCollectedBig":101,"countStolenBig":11,"countCollectedSmall":335,"countStolenSmall":71,"amountOverfill":1773,"amountOverfillStolen":155,"amountUsedWhileSupersonic":1534,"timeZeroBoost":181.47000000000003,"percentZeroBoost":10.4148338,"timeFullBoost":131.05,"percentFullBoost":7.543197340000001,"timeBoost0To25":651.3699999999999,"timeBoost25To50":388.08,"timeBoost50To75":347.99,"timeBoost75To100":357.25,"percentBoost0To25":37.0463032,"percentBoost25To50":22.312887999999997,"percentBoost50To75":20.0812702,"percentBoost75To100":20.5595392},"movement":{"avgSpeed":7742,"totalDistance":2621352,"timeSupersonicSpeed":269.23,"timeBoostSpeed":721.69,"timeSlowSpeed":810.72,"timeGround":1036.6100000000001,"timeLowAir":673.37,"timeHighAir":91.64000000000001,"timePowerslide":33.19,"countPowerslide":316,"avgPowerslideDuration":0.52,"avgSpeedPercentage":67.3217396,"percentSlowSpeed":44.8696308,"percentBoostSpeed":40.1060672,"percentSupersonicSpeed":15.024302599999999,"percentGround":57.65005240000001,"percentLowAir":37.3454176,"percentHighAir":5.004530119999999},"positioning":{"avgDistanceToBall":14158,"avgDistanceToBallPossession":13841,"avgDistanceToBallNoPossession":14319,"avgDistanceToMates":18319,"timeDefensiveThird":980.82,"timeNeutralThird":525.17,"timeOffensiveThird":295.62,"timeDefensiveHalf":1286.43,"timeOffensiveHalf":515.2,"timeBehindBall":1386.24,"timeInfrontBall":415.39,"timeMostBack":679.5,"timeMostForward":501.8999999999999,"goalsAgainstWhileLastDefender":4,"timeClosestToBall":605.4,"timeFarthestFromBall":561.3,"percentDefensiveThird":54.3423058,"percentOffensiveThird":16.3939772,"percentNeutralThird":29.2637188,"percentDefensiveHalf":71.43109100000001,"percentOffensiveHalf":28.5689082,"percentBehindBall":77.080933,"percentInfrontBall":22.9190702,"percentMostBack":39.249312399999994,"percentMostForward":28.789068200000003,"percentClosestToBall":34.863955399999995,"percentFarthestFromBall":32.1834352},"demo":{"inflicted":4,"taken":7}},"advanced":{"goalParticipation":72.72727272727273,"rating":1.1888743076507535}},{"player":{"_id":"60c46906490bd8649dd30e59","slug":"0e59-specs","tag":"Specs","country":"za"},"stats":{"core":{"shots":9,"goals":1,"saves":12,"assists":6,"score":1956,"shootingPercentage":11.11111111111111},"boost":{"bpm":1868,"bcpm":1912.3766,"avgAmount":231.26000000000002,"amountCollected":11068,"amountStolen":2333,"amountCollectedBig":7981,"amountStolenBig":1334,"amountCollectedSmall":3087,"amountStolenSmall":999,"countCollectedBig":91,"countStolenBig":16,"countCollectedSmall":274,"countStolenSmall":87,"amountOverfill":1176,"amountOverfillStolen":272,"amountUsedWhileSupersonic":1238,"timeZeroBoost":165.16,"percentZeroBoost":9.617701,"timeFullBoost":153.71,"percentFullBoost":8.7678304,"timeBoost0To25":574.81,"timeBoost25To50":470.13,"timeBoost50To75":317.99,"timeBoost75To100":367.56,"percentBoost0To25":33.238465000000005,"percentBoost25To50":27.2230186,"percentBoost50To75":18.364024800000003,"percentBoost75To100":21.1744888},"movement":{"avgSpeed":7507,"totalDistance":2517054,"timeSupersonicSpeed":231.07,"timeBoostSpeed":663.18,"timeSlowSpeed":887.45,"timeGround":1042.3300000000002,"timeLowAir":645.3299999999999,"timeHighAir":94.06,"timePowerslide":30.6,"countPowerslide":267,"avgPowerslideDuration":0.57,"avgSpeedPercentage":65.27825999999999,"percentSlowSpeed":49.7449596,"percentBoostSpeed":37.315744,"percentSupersonicSpeed":12.9392956,"percentGround":58.46137839999999,"percentLowAir":36.203643799999995,"percentHighAir":5.334977540000001},"positioning":{"avgDistanceToBall":14010,"avgDistanceToBallPossession":13960,"avgDistanceToBallNoPossession":14037,"avgDistanceToMates":18395,"timeDefensiveThird":863.15,"timeNeutralThird":565.26,"timeOffensiveThird":353.3,"timeDefensiveHalf":1161.6,"timeOffensiveHalf":620.11,"timeBehindBall":1262.05,"timeInfrontBall":519.66,"timeMostBack":539.1,"timeMostForward":607.3,"goalsAgainstWhileLastDefender":3,"timeClosestToBall":609.5,"timeFarthestFromBall":568.1,"percentDefensiveThird":48.257901399999994,"percentOffensiveThird":19.8001306,"percentNeutralThird":31.941968199999998,"percentDefensiveHalf":65.12835279999999,"percentOffensiveHalf":34.8716512,"percentBehindBall":70.9202102,"percentInfrontBall":29.0797904,"percentMostBack":30.8790702,"percentMostForward":34.9347784,"percentClosestToBall":35.1016644,"percentFarthestFromBall":32.636704599999995},"demo":{"inflicted":5,"taken":13}},"advanced":{"goalParticipation":63.63636363636363,"rating":0.9774183280978175}},{"player":{"_id":"629b206ada9d7ca1c7bb1c21","slug":"1c21-nuke","tag":"Nuke","country":"za"},"stats":{"core":{"shots":8,"goals":4,"saves":8,"assists":1,"score":1605,"shootingPercentage":50},"boost":{"bpm":1949,"bcpm":1992.5615900000003,"avgAmount":244.87,"amountCollected":11459,"amountStolen":2344,"amountCollectedBig":7683,"amountStolenBig":1300,"amountCollectedSmall":3776,"amountStolenSmall":1044,"countCollectedBig":95,"countStolenBig":14,"countCollectedSmall":327,"countStolenSmall":85,"amountOverfill":1774,"amountOverfillStolen":111,"amountUsedWhileSupersonic":1182,"timeZeroBoost":192.86,"percentZeroBoost":11.1832152,"timeFullBoost":182.81,"percentFullBoost":10.506658400000001,"timeBoost0To25":652.8299999999999,"timeBoost25To50":394.63,"timeBoost50To75":303.7,"timeBoost75To100":381.18,"percentBoost0To25":37.632627,"percentBoost25To50":22.831935400000003,"percentBoost50To75":17.3806064,"percentBoost75To100":22.1548304},"movement":{"avgSpeed":7759,"totalDistance":2601902,"timeSupersonicSpeed":314.87,"timeBoostSpeed":664.36,"timeSlowSpeed":801.39,"timeGround":981.1700000000001,"timeLowAir":721.77,"timeHighAir":77.69,"timePowerslide":31.3,"countPowerslide":363,"avgPowerslideDuration":0.44000000000000006,"avgSpeedPercentage":67.4695644,"percentSlowSpeed":44.9276014,"percentBoostSpeed":37.401513800000004,"percentSupersonicSpeed":17.670882,"percentGround":55.038424199999994,"percentLowAir":40.5140198,"percentHighAir":4.44755914},"positioning":{"avgDistanceToBall":15839,"avgDistanceToBallPossession":14871,"avgDistanceToBallNoPossession":16762,"avgDistanceToMates":19253,"timeDefensiveThird":878.78,"timeNeutralThird":554.83,"timeOffensiveThird":347.03,"timeDefensiveHalf":1180.32,"timeOffensiveHalf":600.3199999999999,"timeBehindBall":1230.56,"timeInfrontBall":550.0699999999999,"timeMostBack":598.7,"timeMostForward":632.8000000000001,"goalsAgainstWhileLastDefender":5,"timeClosestToBall":526.4000000000001,"timeFarthestFromBall":694.2,"percentDefensiveThird":49.1894626,"percentOffensiveThird":19.599821,"percentNeutralThird":31.2107168,"percentDefensiveHalf":66.1616658,"percentOffensiveHalf":33.838335199999996,"percentBehindBall":69.0119372,"percentInfrontBall":30.988061000000005,"percentMostBack":34.3032278,"percentMostForward":36.458752600000004,"percentClosestToBall":30.168230599999998,"percentFarthestFromBall":40.0620742},"demo":{"inflicted":5,"taken":13}},"advanced":{"goalParticipation":45.45454545454545,"rating":0.9792013297356166}}]},"number":2,"games":[{"_id":"63f8d3b75a20c5676ad5026f","blue":1,"orange":3,"duration":300,"ballchasing":"7fad99a3-2e73-40c5-aee4-afc32d9ef8af"},{"_id":"63f8d5d2f73a2c40ba0427e9","blue":3,"orange":2,"duration":362,"overtime":true,"ballchasing":"1f7e3b09-a00e-4a20-9e00-2754b0d940f2"},{"_id":"63f8d77af73a2c40ba042fb5","blue":3,"orange":2,"duration":300,"ballchasing":"6cf4f178-9a0a-47b0-850b-6ae667bbde3e"},{"_id":"63f8d9565a20c5676ad51014","blue":3,"orange":4,"duration":313,"overtime":true,"ballchasing":"71ccc9de-93f2-472f-bbf8-42e509df3bb1"},{"_id":"63f8db02f73a2c40ba043974","blue":2,"orange":0,"duration":300,"ballchasing":"034523f3-bb29-40d6-8cc2-ea14fcf725ac"}]}],"page":1,"perPage":2,"pageSize":2} \ No newline at end of file diff --git a/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.ios.kt b/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.ios.kt new file mode 100644 index 000000000..34a43f009 --- /dev/null +++ b/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/HttpClientEngine.ios.kt @@ -0,0 +1,8 @@ +package com.adammcneilly.pocketleague.shared.app.data.remote + +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.darwin.Darwin + +actual fun httpClientEngine(): HttpClientEngine { + return Darwin.create() +} diff --git a/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.ios.kt b/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.ios.kt new file mode 100644 index 000000000..ff9a35960 --- /dev/null +++ b/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.ios.kt @@ -0,0 +1,10 @@ +package com.adammcneilly.pocketleague.shared.app.debug + +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.toKString +import platform.posix.getenv + +@OptIn(ExperimentalForeignApi::class) +actual fun getEnv( + name: String, +): String? = getenv(name)?.toKString() From 58c58fc0700e3b12555b05fccad9c20195d573b9 Mon Sep 17 00:00:00 2001 From: Adam McNeilly Date: Sun, 1 Dec 2024 17:37:43 -0500 Subject: [PATCH 2/6] Stubbing debug data onto feed screen. --- config/detekt/detekt.yml | 2 +- shared/app/build.gradle.kts | 3 ++- .../shared/app/debug/GetEnv.android.kt | 5 ---- .../files}/match_detail.json | 0 .../files}/match_list.json | 0 .../app/data/octanegg/OctaneGGKtorClient.kt | 10 +++++--- .../shared/app/debug/DebugHttpClientEngine.kt | 3 ++- .../pocketleague/shared/app/debug/GetEnv.kt | 8 ------- .../pocketleague/shared/app/debug/ReadFile.kt | 16 +++++++++++++ .../shared/app/debug/ReadTestData.kt | 19 --------------- .../shared/app/di/RemoteModule.kt | 23 +++++++++++++++++-- .../shared/app/debug/GetEnv.ios.kt | 10 -------- 12 files changed, 49 insertions(+), 50 deletions(-) delete mode 100644 shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.android.kt rename shared/app/src/commonMain/{resources => composeResources/files}/match_detail.json (100%) rename shared/app/src/commonMain/{resources => composeResources/files}/match_list.json (100%) delete mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadFile.kt delete mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadTestData.kt delete mode 100644 shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.ios.kt diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 8311e1087..ac2a48aec 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -71,7 +71,7 @@ comments: searchInInnerObject: true searchInInnerInterface: true UndocumentedPublicFunction: - active: true + active: false excludes: [ '**/com/adammcneilly/pocketleague/data/local/sqldelight/mappers/**', '**/com/adammcneilly/pocketleague/baselineprofile**', diff --git a/shared/app/build.gradle.kts b/shared/app/build.gradle.kts index 5e2a8a426..76e973202 100644 --- a/shared/app/build.gradle.kts +++ b/shared/app/build.gradle.kts @@ -23,10 +23,11 @@ kotlin { sourceSets { commonMain.dependencies { - implementation(compose.runtime) + implementation(compose.components.resources) implementation(compose.foundation) implementation(compose.material3) implementation(compose.materialIconsExtended) + implementation(compose.runtime) implementation(libs.androidx.lifecycle.viewmodel) implementation(libs.androidx.navigation.compose) implementation(libs.cketti.codepoints) diff --git a/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.android.kt b/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.android.kt deleted file mode 100644 index f084da283..000000000 --- a/shared/app/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.android.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.adammcneilly.pocketleague.shared.app.debug - -actual fun getEnv( - name: String, -): String? = System.getenv(name) diff --git a/shared/app/src/commonMain/resources/match_detail.json b/shared/app/src/commonMain/composeResources/files/match_detail.json similarity index 100% rename from shared/app/src/commonMain/resources/match_detail.json rename to shared/app/src/commonMain/composeResources/files/match_detail.json diff --git a/shared/app/src/commonMain/resources/match_list.json b/shared/app/src/commonMain/composeResources/files/match_list.json similarity index 100% rename from shared/app/src/commonMain/resources/match_list.json rename to shared/app/src/commonMain/composeResources/files/match_list.json diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/octanegg/OctaneGGKtorClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/octanegg/OctaneGGKtorClient.kt index 63c03aad2..d72b35d73 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/octanegg/OctaneGGKtorClient.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/octanegg/OctaneGGKtorClient.kt @@ -1,10 +1,14 @@ package com.adammcneilly.pocketleague.shared.app.data.octanegg import com.adammcneilly.pocketleague.shared.app.data.remote.BaseKtorClient +import io.ktor.client.HttpClient /** * An instance of a [BaseKtorClient] that makes all requests to the octane.gg API. */ -object OctaneGGKtorClient : BaseKtorClient( - baseURL = "https://zsr.octane.gg/", -) +class OctaneGGKtorClient( + httpClient: HttpClient, +) : BaseKtorClient( + baseURL = "https://zsr.octane.gg/", + httpClient = httpClient, + ) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt index 9ff54472a..449344d4e 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt @@ -15,7 +15,8 @@ import io.ktor.utils.io.ByteReadChannel fun debugHttpClientEngine( responses: Map, ) = MockEngine { - val url = it.url.fullPath + // In a debug situation, we don't need params. + val url = it.url.fullPath.substringBefore("?") val response = responses[url] diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.kt deleted file mode 100644 index db3fab8cc..000000000 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.adammcneilly.pocketleague.shared.app.debug - -/** - * Retrieve an environment variable with a given [name]. - */ -expect fun getEnv( - name: String, -): String? diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadFile.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadFile.kt new file mode 100644 index 000000000..1411b4344 --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadFile.kt @@ -0,0 +1,16 @@ +package com.adammcneilly.pocketleague.shared.app.debug + +import okio.internal.commonToUtf8String +import org.jetbrains.compose.resources.ExperimentalResourceApi +import pocketleague.shared.app.generated.resources.Res + +/** + * Reads the data from a file with the given [fileName]. + */ +@OptIn(ExperimentalResourceApi::class) +suspend fun readFile( + fileName: String, +): String { + val readBytes = Res.readBytes(fileName) + return readBytes.commonToUtf8String() +} diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadTestData.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadTestData.kt deleted file mode 100644 index a3a3ce362..000000000 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/ReadTestData.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.adammcneilly.pocketleague.shared.app.debug - -import okio.FileSystem -import okio.Path.Companion.toPath -import okio.SYSTEM - -/** - * Reads the test data from a file with the given [fileName]. - */ -fun readTestData( - fileName: String, -): String { - val root = getEnv("TEST_DATA_ROOT")!! - val fullPath = "$root/$fileName".toPath() - - return FileSystem.SYSTEM.read(fullPath) { - readUtf8() - } -} diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt index 28ae5aeea..d7c9b7c3d 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt @@ -1,14 +1,33 @@ package com.adammcneilly.pocketleague.shared.app.di -import com.adammcneilly.pocketleague.shared.app.data.octanegg.OctaneGGKtorClient import com.adammcneilly.pocketleague.shared.app.data.remote.BaseKtorClient +import com.adammcneilly.pocketleague.shared.app.data.remote.defaultHttpClient +import com.adammcneilly.pocketleague.shared.app.debug.debugHttpClientEngine +import com.adammcneilly.pocketleague.shared.app.debug.readFile +import kotlinx.coroutines.runBlocking import org.koin.core.qualifier.named import org.koin.dsl.module const val OCTANEGG_CLIENT = "octanegg" +const val MOCK_OCTANE_CLIENT_ENGINE = "mock_octanegg_engine" val remoteModule = module { single(named(OCTANEGG_CLIENT)) { - OctaneGGKtorClient + runBlocking { + val matchListJson = readFile("files/match_list.json") + val matchDetailJson = readFile("files/match_detail.json") + + val engine = debugHttpClientEngine( + responses = mapOf( + "/matches" to matchListJson, + "/matches/123" to matchDetailJson, + ), + ) + + BaseKtorClient( + baseURL = "", + httpClient = defaultHttpClient(engine), + ) + } } } diff --git a/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.ios.kt b/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.ios.kt deleted file mode 100644 index ff9a35960..000000000 --- a/shared/app/src/iosMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/GetEnv.ios.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.adammcneilly.pocketleague.shared.app.debug - -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.toKString -import platform.posix.getenv - -@OptIn(ExperimentalForeignApi::class) -actual fun getEnv( - name: String, -): String? = getenv(name)?.toKString() From df30fbfcf5faaed71b33ff536fe0279ffd475d24 Mon Sep 17 00:00:00 2001 From: Adam McNeilly Date: Sun, 1 Dec 2024 17:51:14 -0500 Subject: [PATCH 3/6] Successfully requesting test data. --- ...DefaultHttpClient.kt => BaseHttpClient.kt} | 30 ++++---- .../shared/app/data/remote/BaseKtorClient.kt | 2 +- .../shared/app/debug/DebugHttpClient.kt | 10 --- .../shared/app/debug/DebugHttpClientEngine.kt | 10 +-- .../shared/app/debug/DebugKtorClient.kt | 10 --- .../pocketleague/shared/app/di/AllModules.kt | 3 +- .../pocketleague/shared/app/di/DebugModule.kt | 12 ++++ .../shared/app/di/RemoteModule.kt | 68 +++++++++++++------ 8 files changed, 85 insertions(+), 60 deletions(-) rename shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/{DefaultHttpClient.kt => BaseHttpClient.kt} (60%) delete mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClient.kt delete mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugKtorClient.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/DebugModule.kt diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/DefaultHttpClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseHttpClient.kt similarity index 60% rename from shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/DefaultHttpClient.kt rename to shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseHttpClient.kt index c457b3dca..f9d8d080b 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/DefaultHttpClient.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseHttpClient.kt @@ -15,19 +15,21 @@ import kotlinx.serialization.json.Json * A default implementation of an [HttpClient] that will use an engine defined * by the platform it's being used on. This allows us to override for tests. */ -fun defaultHttpClient( - engine: HttpClientEngine = httpClientEngine(), -) = HttpClient(engine) { - install(ContentNegotiation) { - val converter = KotlinxSerializationConverter( - Json { - ignoreUnknownKeys = true - }, - ) - register(ContentType.Any, converter) - } - install(Logging) { - logger = Logger.SIMPLE - level = LogLevel.ALL +fun baseHttpClient( + engine: HttpClientEngine, +): HttpClient { + return HttpClient(engine) { + install(ContentNegotiation) { + val converter = KotlinxSerializationConverter( + Json { + ignoreUnknownKeys = true + }, + ) + register(ContentType.Any, converter) + } + install(Logging) { + logger = Logger.SIMPLE + level = LogLevel.ALL + } } } diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseKtorClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseKtorClient.kt index 7ed1f8066..eacef1585 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseKtorClient.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/remote/BaseKtorClient.kt @@ -21,7 +21,7 @@ typealias RemoteParams = Map */ open class BaseKtorClient( val baseURL: String, - val httpClient: HttpClient = defaultHttpClient(), + val httpClient: HttpClient, ) { /** * A helper function to build the [baseURL] and [endpoint] operation and performs a get request. diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClient.kt deleted file mode 100644 index 1fffc050a..000000000 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClient.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.adammcneilly.pocketleague.shared.app.debug - -import com.adammcneilly.pocketleague.shared.app.data.remote.defaultHttpClient - -/** - * Creates an implementation of our [defaultHttpClient] that uses a [mockEngine]. - */ -fun debugHttpClient( - responses: Map, -) = defaultHttpClient(debugHttpClientEngine(responses)) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt index 449344d4e..117133440 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugHttpClientEngine.kt @@ -10,7 +10,7 @@ import io.ktor.utils.io.ByteReadChannel /** * Create a custom [MockEngine] that will determine the response to return based on the supplied - * mock [responses]. + * mock [responses], which is a key value pair of url to response file names. */ fun debugHttpClientEngine( responses: Map, @@ -18,9 +18,9 @@ fun debugHttpClientEngine( // In a debug situation, we don't need params. val url = it.url.fullPath.substringBefore("?") - val response = responses[url] + val responseFile = responses[url] - if (response == null) { + if (responseFile == null) { throw IllegalArgumentException( """ No mock response found for url: $url @@ -30,8 +30,10 @@ fun debugHttpClientEngine( ) } + val responseText = readFile(responseFile) + respond( - content = ByteReadChannel(response), + content = ByteReadChannel(responseText), status = HttpStatusCode.OK, headers = headersOf(HttpHeaders.ContentType, "application/json"), ) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugKtorClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugKtorClient.kt deleted file mode 100644 index 2084479d6..000000000 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/debug/DebugKtorClient.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.adammcneilly.pocketleague.shared.app.debug - -import com.adammcneilly.pocketleague.shared.app.data.remote.BaseKtorClient - -class DebugKtorClient( - mockResponses: Map = emptyMap(), -) : BaseKtorClient( - baseURL = "", - httpClient = debugHttpClient(mockResponses), - ) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/AllModules.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/AllModules.kt index b0fd0e894..020eca37a 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/AllModules.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/AllModules.kt @@ -1,9 +1,10 @@ package com.adammcneilly.pocketleague.shared.app.di val allModules = listOf( - utilModule, + debugModule, remoteModule, repositoryModule, useCaseModule, + utilModule, viewModelModule, ) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/DebugModule.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/DebugModule.kt new file mode 100644 index 000000000..a7addafe1 --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/DebugModule.kt @@ -0,0 +1,12 @@ +package com.adammcneilly.pocketleague.shared.app.di + +import org.koin.core.qualifier.named +import org.koin.dsl.module + +const val USE_DEBUG_DATA = "use_debug_data" + +val debugModule = module { + single(named(USE_DEBUG_DATA)) { + true + } +} diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt index d7c9b7c3d..0b33b3fad 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt @@ -1,33 +1,61 @@ package com.adammcneilly.pocketleague.shared.app.di import com.adammcneilly.pocketleague.shared.app.data.remote.BaseKtorClient -import com.adammcneilly.pocketleague.shared.app.data.remote.defaultHttpClient +import com.adammcneilly.pocketleague.shared.app.data.remote.baseHttpClient +import com.adammcneilly.pocketleague.shared.app.data.remote.httpClientEngine import com.adammcneilly.pocketleague.shared.app.debug.debugHttpClientEngine -import com.adammcneilly.pocketleague.shared.app.debug.readFile -import kotlinx.coroutines.runBlocking +import io.ktor.client.HttpClient +import io.ktor.client.engine.HttpClientEngine import org.koin.core.qualifier.named import org.koin.dsl.module const val OCTANEGG_CLIENT = "octanegg" -const val MOCK_OCTANE_CLIENT_ENGINE = "mock_octanegg_engine" +const val DEFAULT_ENGINE = "default_engine" +const val MOCK_OCTANEGG_ENGINE = "mock_octanegg_engine" +const val OCTANE_GG_BASE_URL = "octanegg_base_url" val remoteModule = module { - single(named(OCTANEGG_CLIENT)) { - runBlocking { - val matchListJson = readFile("files/match_list.json") - val matchDetailJson = readFile("files/match_detail.json") - - val engine = debugHttpClientEngine( - responses = mapOf( - "/matches" to matchListJson, - "/matches/123" to matchDetailJson, - ), - ) - - BaseKtorClient( - baseURL = "", - httpClient = defaultHttpClient(engine), - ) + single(named(MOCK_OCTANEGG_ENGINE)) { + debugHttpClientEngine( + responses = mapOf( + "/matches" to "files/match_list.json", + "/matches/123" to "files/match_detail.json", + ), + ) + } + + single(named(DEFAULT_ENGINE)) { + httpClientEngine() + } + + single(named(OCTANE_GG_BASE_URL)) { + val useDebugData = get(named(USE_DEBUG_DATA)) + + if (useDebugData) { + "" + } else { + "https://zsr.octane.gg/" + } + } + + single { + val useDebugData = get(named(USE_DEBUG_DATA)) + + val engine = if (useDebugData) { + get(named(MOCK_OCTANEGG_ENGINE)) + } else { + get(named(DEFAULT_ENGINE)) } + + baseHttpClient( + engine = engine, + ) + } + + single(named(OCTANEGG_CLIENT)) { + BaseKtorClient( + baseURL = get(named(OCTANE_GG_BASE_URL)), + httpClient = get(), + ) } } From f916c210bacd6dccb45aacca724f70058ce8ac24 Mon Sep 17 00:00:00 2001 From: Adam McNeilly Date: Sun, 1 Dec 2024 17:57:25 -0500 Subject: [PATCH 4/6] Adding docs. --- .../app/data/octanegg/OctaneGGKtorClient.kt | 14 ---- .../pocketleague/shared/app/di/AllModules.kt | 2 +- .../shared/app/di/OctaneGGModule.kt | 74 +++++++++++++++++++ .../shared/app/di/RemoteModule.kt | 61 --------------- 4 files changed, 75 insertions(+), 76 deletions(-) delete mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/octanegg/OctaneGGKtorClient.kt create mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/OctaneGGModule.kt delete mode 100644 shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/octanegg/OctaneGGKtorClient.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/octanegg/OctaneGGKtorClient.kt deleted file mode 100644 index d72b35d73..000000000 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/data/octanegg/OctaneGGKtorClient.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.adammcneilly.pocketleague.shared.app.data.octanegg - -import com.adammcneilly.pocketleague.shared.app.data.remote.BaseKtorClient -import io.ktor.client.HttpClient - -/** - * An instance of a [BaseKtorClient] that makes all requests to the octane.gg API. - */ -class OctaneGGKtorClient( - httpClient: HttpClient, -) : BaseKtorClient( - baseURL = "https://zsr.octane.gg/", - httpClient = httpClient, - ) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/AllModules.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/AllModules.kt index 020eca37a..97e992299 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/AllModules.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/AllModules.kt @@ -2,7 +2,7 @@ package com.adammcneilly.pocketleague.shared.app.di val allModules = listOf( debugModule, - remoteModule, + octaneGGModule, repositoryModule, useCaseModule, utilModule, diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/OctaneGGModule.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/OctaneGGModule.kt new file mode 100644 index 000000000..73daf235d --- /dev/null +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/OctaneGGModule.kt @@ -0,0 +1,74 @@ +package com.adammcneilly.pocketleague.shared.app.di + +import com.adammcneilly.pocketleague.shared.app.data.remote.BaseKtorClient +import com.adammcneilly.pocketleague.shared.app.data.remote.baseHttpClient +import com.adammcneilly.pocketleague.shared.app.data.remote.httpClientEngine +import com.adammcneilly.pocketleague.shared.app.debug.debugHttpClientEngine +import io.ktor.client.HttpClient +import io.ktor.client.engine.HttpClientEngine +import org.koin.core.qualifier.named +import org.koin.dsl.module + +private const val MOCK_OCTANE_ENGINE = "mock_octane_engine" +private const val OCTANE_BASE_URL = "octane_base_url" +private const val OCTANE_HTTP_CLIENT = "octane_http_client" +const val OCTANE_GG_CLIENT = "octane_gg_ktor_client" + +val octaneGGModule = module { + /** + * Defines a [debugHttpClientEngine] for the octane GG api. + */ + single(named(MOCK_OCTANE_ENGINE)) { + debugHttpClientEngine( + responses = mapOf( + "/matches" to "files/match_list.json", + "/matches/123" to "files/match_detail.json", + ), + ) + } + + /** + * Defines the base url for the octane GG api. If using debug data, + * we should not use a real base URL but just an empty string. + */ + single(named(OCTANE_BASE_URL)) { + val useDebugData = get(named(USE_DEBUG_DATA)) + + if (useDebugData) { + "" + } else { + "https://zsr.octane.gg/" + } + } + + /** + * If using debug data, we'll supplied our [MOCK_OCTANE_ENGINE] entry. + * Otherwise, default to the platform specific engine. + */ + single(named(OCTANE_HTTP_CLIENT)) { + val useDebugData = get(named(USE_DEBUG_DATA)) + + val engine = if (useDebugData) { + get(named(MOCK_OCTANE_ENGINE)) + } else { + httpClientEngine() + } + + baseHttpClient( + engine = engine, + ) + } + + /** + * Creates a [BaseKtorClient] using the necessary base URL and http client engines. + * + * The specific factories for those dependencies will create with debug implementations, + * if necessary. + */ + single(named(OCTANE_GG_CLIENT)) { + BaseKtorClient( + baseURL = get(named(OCTANE_BASE_URL)), + httpClient = get(named(OCTANE_HTTP_CLIENT)), + ) + } +} diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt deleted file mode 100644 index 0b33b3fad..000000000 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RemoteModule.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.adammcneilly.pocketleague.shared.app.di - -import com.adammcneilly.pocketleague.shared.app.data.remote.BaseKtorClient -import com.adammcneilly.pocketleague.shared.app.data.remote.baseHttpClient -import com.adammcneilly.pocketleague.shared.app.data.remote.httpClientEngine -import com.adammcneilly.pocketleague.shared.app.debug.debugHttpClientEngine -import io.ktor.client.HttpClient -import io.ktor.client.engine.HttpClientEngine -import org.koin.core.qualifier.named -import org.koin.dsl.module - -const val OCTANEGG_CLIENT = "octanegg" -const val DEFAULT_ENGINE = "default_engine" -const val MOCK_OCTANEGG_ENGINE = "mock_octanegg_engine" -const val OCTANE_GG_BASE_URL = "octanegg_base_url" - -val remoteModule = module { - single(named(MOCK_OCTANEGG_ENGINE)) { - debugHttpClientEngine( - responses = mapOf( - "/matches" to "files/match_list.json", - "/matches/123" to "files/match_detail.json", - ), - ) - } - - single(named(DEFAULT_ENGINE)) { - httpClientEngine() - } - - single(named(OCTANE_GG_BASE_URL)) { - val useDebugData = get(named(USE_DEBUG_DATA)) - - if (useDebugData) { - "" - } else { - "https://zsr.octane.gg/" - } - } - - single { - val useDebugData = get(named(USE_DEBUG_DATA)) - - val engine = if (useDebugData) { - get(named(MOCK_OCTANEGG_ENGINE)) - } else { - get(named(DEFAULT_ENGINE)) - } - - baseHttpClient( - engine = engine, - ) - } - - single(named(OCTANEGG_CLIENT)) { - BaseKtorClient( - baseURL = get(named(OCTANE_GG_BASE_URL)), - httpClient = get(), - ) - } -} From c18cb631607553e9f5ff0af37cca31f7b4db5eec Mon Sep 17 00:00:00 2001 From: Adam McNeilly Date: Mon, 2 Dec 2024 15:02:48 -0500 Subject: [PATCH 5/6] Fixing module reference. --- .../pocketleague/shared/app/di/RepositoryModule.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RepositoryModule.kt b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RepositoryModule.kt index 4ef0f0439..f1c57940d 100644 --- a/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RepositoryModule.kt +++ b/shared/app/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/app/di/RepositoryModule.kt @@ -10,13 +10,13 @@ import org.koin.dsl.module val repositoryModule = module { single { OctaneGGEventRepository( - apiClient = get(named(OCTANEGG_CLIENT)), + apiClient = get(named(OCTANE_GG_CLIENT)), ) } single { OctaneGGMatchRepository( - apiClient = get(named(OCTANEGG_CLIENT)), + apiClient = get(named(OCTANE_GG_CLIENT)), ) } } From 7b44770f14e8a99336ff9ba0ecbcf8b269b3f367 Mon Sep 17 00:00:00 2001 From: Adam McNeilly Date: Mon, 2 Dec 2024 15:17:40 -0500 Subject: [PATCH 6/6] Excluding build folder. --- shared/app/build.gradle.kts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/shared/app/build.gradle.kts b/shared/app/build.gradle.kts index 76e973202..5728d10af 100644 --- a/shared/app/build.gradle.kts +++ b/shared/app/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jmailen.gradle.kotlinter.tasks.FormatTask +import org.jmailen.gradle.kotlinter.tasks.LintTask + plugins { kotlin("multiplatform") kotlin("plugin.serialization") @@ -68,3 +71,11 @@ android { namespace = "com.adammcneilly.pocketleague.shared.app" } +tasks.withType { + exclude { it.file.path.contains("build/")} +} + +tasks.withType { + exclude { it.file.path.contains("build/")} +} +