Skip to content
Snippets Groups Projects
Unverified Commit 4937ffc2 authored by Chilja Gossow's avatar Chilja Gossow Committed by GitHub
Browse files

Test menu additions for download, matching, risk calculation (#2607)


* matching

* hook to ui

* add fake data sources

* string output

* lint

* test fragment

* merge main branch

* add toast

* klint

* detekt

* adaptation for split

* klint

* add local date

Co-authored-by: default avatarMohamed Metwalli <mohamed.metwalli@sap.com>
Co-authored-by: default avatarMatthias Urhahn <matthias.urhahn@sap.com>
parent 625a2a4e
No related branches found
No related tags found
No related merge requests found
Showing
with 239 additions and 12 deletions
......@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.test.eventregistration.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import de.rki.coronawarnapp.R
......@@ -10,6 +11,7 @@ import de.rki.coronawarnapp.databinding.FragmentTestEventregistrationBinding
import de.rki.coronawarnapp.test.menu.ui.TestMenuItem
import de.rki.coronawarnapp.util.di.AutoInject
import de.rki.coronawarnapp.util.ui.doNavigate
import de.rki.coronawarnapp.util.ui.observe2
import de.rki.coronawarnapp.util.ui.viewBindingLazy
import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
......@@ -49,6 +51,29 @@ class EventRegistrationTestFragment : Fragment(R.layout.fragment_test_eventregis
findNavController().navigate(R.id.showStoredEventsTestFragment)
}
}
binding.runMatcher.setOnClickListener {
Toast.makeText(context, "Not implemented", Toast.LENGTH_SHORT).show()
}
binding.downloadReportedCheckIns.setOnClickListener {
Toast.makeText(context, "Not implemented", Toast.LENGTH_SHORT).show()
}
binding.calculateRisk.setOnClickListener {
Toast.makeText(context, "Not implemented", Toast.LENGTH_SHORT).show()
}
viewModel.checkInOverlaps.observe2(this) {
val text = it.fold(StringBuilder()) { stringBuilder, checkInOverlap ->
stringBuilder
.append("CheckIn Id ${checkInOverlap.checkInId}")
.append("Date ${checkInOverlap.localDate}")
.append("Min. ${checkInOverlap.overlap.standardMinutes}")
.append("\n")
}
binding.resultText.text = text
binding.resultText.visibility = View.VISIBLE
}
}
companion object {
......
package de.rki.coronawarnapp.test.eventregistration.ui
import androidx.lifecycle.MutableLiveData
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import de.rki.coronawarnapp.eventregistration.checkins.riskcalculation.CheckInMatcher
import de.rki.coronawarnapp.eventregistration.checkins.riskcalculation.CheckInOverlap
import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
class EventRegistrationTestFragmentViewModel @AssistedInject constructor(
dispatcherProvider: DispatcherProvider
private val dispatcherProvider: DispatcherProvider,
private val checkInMatcher: CheckInMatcher
) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
val checkInOverlaps = MutableLiveData<List<CheckInOverlap>>()
fun runMatcher() {
launch {
val overlaps = checkInMatcher.execute()
checkInOverlaps.postValue(overlaps)
}
}
@AssistedFactory
interface Factory : SimpleCWAViewModelFactory<EventRegistrationTestFragmentViewModel>
}
......@@ -10,17 +10,13 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="vertical"
android:paddingBottom="32dp">
android:orientation="vertical">
<LinearLayout
style="@style/Card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_tiny"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:orientation="vertical">
<TextView
......@@ -32,7 +28,7 @@
android:id="@+id/testQrCodeCreation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginTop="@dimen/spacing_tiny"
android:text="QR Code Creation" />
</LinearLayout>
......@@ -41,10 +37,10 @@
style="@style/Card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_tiny"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginHorizontal="@dimen/spacing_tiny"
android:layout_marginBottom="@dimen/spacing_tiny"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
......@@ -54,10 +50,54 @@
android:id="@+id/scanCheckInQrCode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_tiny"
android:text="Scan check in QR code" />
</LinearLayout>
<LinearLayout
style="@style/Card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/spacing_tiny"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Download, matching &amp; risk calculation" />
<com.google.android.material.button.MaterialButton
android:id="@+id/downloadReportedCheckIns"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_tiny"
android:text="download check-ins" />
<com.google.android.material.button.MaterialButton
android:id="@+id/runMatcher"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:text="Run matcher" />
<TextView
android:id="@+id/resultText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:text="Matching result"
android:visibility="gone" />
<com.google.android.material.button.MaterialButton
android:id="@+id/calculateRisk"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:text="Calculate risk" />
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/event_container"
style="@style/Card"
......@@ -108,7 +148,5 @@
app:layout_constraintStart_toEndOf="@id/create_event_button"
app:layout_constraintTop_toBottomOf="@id/events_body" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
......@@ -2,6 +2,8 @@ package de.rki.coronawarnapp.eventregistration
import dagger.Binds
import dagger.Module
import de.rki.coronawarnapp.eventregistration.checkins.download.DownloadedCheckInsRepo
import de.rki.coronawarnapp.eventregistration.checkins.download.FakeDownloadedCheckInsRepo
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.DefaultQRCodeVerifier
import de.rki.coronawarnapp.eventregistration.checkins.qrcode.QRCodeVerifier
import de.rki.coronawarnapp.eventregistration.storage.repo.DefaultTraceLocationRepository
......@@ -16,4 +18,7 @@ abstract class EventRegistrationModule {
@Binds
abstract fun traceLocationRepository(defaultTraceLocationRepo: DefaultTraceLocationRepository):
TraceLocationRepository
@Binds
abstract fun downloadedCheckInsRepo(repository: FakeDownloadedCheckInsRepo): DownloadedCheckInsRepo
}
package de.rki.coronawarnapp.eventregistration.checkins.download
import de.rki.coronawarnapp.eventregistration.checkins.CheckIn
import org.joda.time.DateTime
import org.joda.time.Instant
interface CheckInsPackage {
......@@ -9,3 +11,57 @@ interface CheckInsPackage {
*/
suspend fun extractCheckIns(): List<CheckIn>
}
// TODO replace with actual implementation
// proprietary dummy implementations
object DummyCheckInPackage : CheckInsPackage {
override suspend fun extractCheckIns(): List<CheckIn> {
return listOf(dummyEventCheckIn1)
}
}
private const val TYPE_LOCATION = 1
private const val TYPE_EVENT = 2
private val dummyEventCheckIn1: CheckIn = CheckIn(
id = 1L,
guid = "eventOne",
version = 1,
type = TYPE_LOCATION,
description = "Restaurant",
address = "Around the corner",
traceLocationStart = null,
traceLocationEnd = null,
defaultCheckInLengthInMinutes = null,
signature = "signature",
checkInStart = Instant.ofEpochMilli(
DateTime(2021, 2, 2012, 11, 45).millis
),
checkInEnd = Instant.ofEpochMilli(
DateTime(2021, 2, 20, 12, 15).millis
),
targetCheckInEnd = null,
createJournalEntry = false
)
private val dummyEventCheckIn2: CheckIn = CheckIn(
id = 1L,
guid = "eventOne",
version = 1,
type = TYPE_EVENT,
description = "Women in tech meetup",
address = "Technology Park",
traceLocationStart = null,
traceLocationEnd = null,
defaultCheckInLengthInMinutes = null,
signature = "signature2",
checkInStart = Instant.ofEpochMilli(
DateTime(2021, 3, 20, 18, 45).millis
),
checkInEnd = Instant.ofEpochMilli(
DateTime(2021, 3, 20, 20, 15).millis
),
targetCheckInEnd = null,
createJournalEntry = false
)
package de.rki.coronawarnapp.eventregistration.checkins.download
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import javax.inject.Inject
interface DownloadedCheckInsRepo {
......@@ -10,3 +12,16 @@ interface DownloadedCheckInsRepo {
fun removeCheckIns(checkins: List<CheckInsPackage>)
}
class FakeDownloadedCheckInsRepo @Inject constructor() : DownloadedCheckInsRepo {
override val allCheckInsPackages: Flow<List<CheckInsPackage>>
get() = listOf(listOf<CheckInsPackage>(DummyCheckInPackage)).asFlow()
override fun addCheckIns(checkins: List<CheckInsPackage>) {
// TODO("Not yet implemented")
}
override fun removeCheckIns(checkins: List<CheckInsPackage>) {
// TODO("Not yet implemented")
}
}
package de.rki.coronawarnapp.eventregistration.checkins.riskcalculation
import de.rki.coronawarnapp.eventregistration.checkins.CheckInRepository
import de.rki.coronawarnapp.eventregistration.checkins.download.DownloadedCheckInsRepo
import kotlinx.coroutines.flow.firstOrNull
import javax.inject.Inject
class CheckInMatcher @Inject constructor(
private val checkInsRepository: CheckInRepository,
private val downloadedCheckInsRepo: DownloadedCheckInsRepo
) {
suspend fun execute(): List<CheckInOverlap> {
val localCheckIns = checkInsRepository.allCheckIns.firstOrNull() ?: return emptyList()
val downloadedPackages = downloadedCheckInsRepo.allCheckInsPackages.firstOrNull() ?: return emptyList()
val relevantDownloadedCheckIns =
downloadedPackages.flatMap {
filterRelevantEventCheckIns(
localCheckIns,
it
)
}
if (relevantDownloadedCheckIns.isEmpty()) return emptyList()
// TODO split by midnight UTC?
// calculate time overlap
val eventOverlapList = mutableListOf<CheckInOverlap>()
relevantDownloadedCheckIns.forEach { relevantDownloadedCheckIn ->
localCheckIns.forEach { localCheckIn ->
val overlap = calculateOverlap(localCheckIn, relevantDownloadedCheckIn)
if (overlap != null) eventOverlapList.add(overlap)
}
}
return eventOverlapList
}
}
package de.rki.coronawarnapp.eventregistration.checkins.riskcalculation
import de.rki.coronawarnapp.eventregistration.checkins.CheckIn
import de.rki.coronawarnapp.eventregistration.checkins.download.CheckInsPackage
import org.joda.time.Duration
import org.joda.time.Instant
import org.joda.time.LocalDate
suspend fun filterRelevantEventCheckIns(
localCheckIns: List<CheckIn>,
checkInsPackage: CheckInsPackage
): List<CheckIn> {
val reportedCheckIns = checkInsPackage.extractCheckIns()
return reportedCheckIns.filter { reported ->
localCheckIns.find { local ->
reported.guid == local.guid
} != null
}
}
fun calculateOverlap(
local: CheckIn,
reported: CheckIn
): CheckInOverlap? {
if (local.guid != reported.guid) return null
// TODO implement calculation
return null
}
data class CheckInOverlap(
val checkInId: Long,
val localDate: LocalDate,
val overlap: Duration
)
fun min(first: Instant, second: Instant) = Instant(kotlin.math.min(first.millis, second.millis))
fun max(first: Instant, second: Instant) = Instant(kotlin.math.max(first.millis, second.millis))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment