Compare commits

..

1 Commits

Author SHA1 Message Date
Pritimay Sarkar
4fef75b937 add new thresholds with additonal method and unit tests 2024-02-06 13:44:00 +05:30
16 changed files with 52 additions and 274 deletions

View File

@@ -14,13 +14,13 @@ android {
compileSdk 34 compileSdk 34
namespace 'in.sminnovations.hpostesting' namespace 'in.sminnovations.hpostesting'
// prod - production, preprod - preproduction, quality - qc, dev - development // dev - development, quality - qc, uat - User Acceptance Test, preprod - preproduction, prod - production
defaultConfig { defaultConfig {
applicationId "in.sminnovations.hpostesting.quality" applicationId "in.sminnovations.hpostesting.quality"
minSdk 21 minSdk 21
targetSdk 34 targetSdk 34
versionCode 99 versionCode 101
versionName "2.1.99" versionName "2.1.101"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -157,7 +157,7 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp:4.9.3") implementation("com.squareup.okhttp3:okhttp:4.9.3")
implementation "androidx.preference:preference-ktx:1.2.1" implementation "androidx.preference:preference-ktx:1.2.1"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1' implementation 'io.nats:jnats:2.11.2'
implementation("androidx.work:work-runtime-ktx:2.9.0") implementation("androidx.work:work-runtime-ktx:2.9.0")
@@ -165,6 +165,4 @@ dependencies {
implementation 'com.google.android.play:core:1.10.3' implementation 'com.google.android.play:core:1.10.3'
implementation fileTree(dir: 'libs', include: ['*.aar']) implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation 'io.nats:jnats:2.11.4'
} }

BIN
app/release/hpos-app.apk Normal file

Binary file not shown.

View File

@@ -4,15 +4,15 @@
"type": "APK", "type": "APK",
"kind": "Directory" "kind": "Directory"
}, },
"applicationId": "in.sminnovations.hpostesting.quality", "applicationId": "com.example.hpos",
"variantName": "release", "variantName": "release",
"elements": [ "elements": [
{ {
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 101, "versionCode": 1,
"versionName": "2.1.101", "versionName": "1.0",
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }
], ],

View File

@@ -119,8 +119,7 @@
android:theme="@style/AppTheme.NoActionBar"> android:theme="@style/AppTheme.NoActionBar">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>

View File

@@ -7,11 +7,8 @@ import com.example.hpostesting.data.model.updates.CheckUpdateRequest
import com.example.hpostesting.data.model.updates.CheckUpdateResponse import com.example.hpostesting.data.model.updates.CheckUpdateResponse
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.Response
import okhttp3.ResponseBody import okhttp3.ResponseBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Multipart import retrofit2.http.Multipart
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.PUT import retrofit2.http.PUT
@@ -34,10 +31,6 @@ interface MolbioResultApi {
@Body deviceUpdateRequest: DeviceUpdateRequest @Body deviceUpdateRequest: DeviceUpdateRequest
): ResponseBody ): ResponseBody
@GET("deviceService/device/getClientCertificate")
suspend fun downloadClientCertificate(
): ResponseBody
@Multipart @Multipart
@POST("deviceService/device/uploadLogs") @POST("deviceService/device/uploadLogs")
suspend fun uploadLogs( suspend fun uploadLogs(

View File

@@ -8,7 +8,7 @@ object Constants {
const val ABHA_APP_PACKAGE = "in.ndhm.phr" const val ABHA_APP_PACKAGE = "in.ndhm.phr"
const val MOLBIO_INTERGATION = false const val MOLBIO_INTEGRATION = false
const val deviceProvisionEmail = "HPOS_provisioner@bigtec.co.in" const val deviceProvisionEmail = "HPOS_provisioner@bigtec.co.in"
const val deviceProvisionPassword = "f2ab0e7f9d69" const val deviceProvisionPassword = "f2ab0e7f9d69"
const val DEVICE_ID_API = "deviceIDAPI" const val DEVICE_ID_API = "deviceIDAPI"

View File

@@ -77,9 +77,6 @@ class DatabaseRepository @Inject constructor(
return safeApiCall { molbioResultApi.deviceUpdate(deviceUpdateRequest) } return safeApiCall { molbioResultApi.deviceUpdate(deviceUpdateRequest) }
} }
override suspend fun downloadClientCertificate(): Result<ResponseBody> {
return safeApiCall { molbioResultApi.downloadClientCertificate() }
}
override suspend fun uploadLogs(logFile: MultipartBody.Part): Result<UploadLogsResponse> { override suspend fun uploadLogs(logFile: MultipartBody.Part): Result<UploadLogsResponse> {
return safeApiCall { molbioResultApi.uploadLogs(logFile) } return safeApiCall { molbioResultApi.uploadLogs(logFile) }
} }

View File

@@ -45,6 +45,5 @@ interface Repository {
suspend fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest): Result<ResponseBody> suspend fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest): Result<ResponseBody>
suspend fun downloadClientCertificate(): Result<ResponseBody>
suspend fun uploadLogs(logFile: MultipartBody.Part): Result<UploadLogsResponse> suspend fun uploadLogs(logFile: MultipartBody.Part): Result<UploadLogsResponse>
} }

View File

@@ -1,9 +1,6 @@
package com.example.hpostesting.presentation package com.example.hpostesting.presentation
import android.os.Build
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi
import com.example.hpostesting.presentation.assurance.AssuranceControlsActivity
import com.example.hpostesting.presentation.dashboard.DashboardActivity import com.example.hpostesting.presentation.dashboard.DashboardActivity
import io.nats.client.AuthHandler import io.nats.client.AuthHandler
import io.nats.client.Connection import io.nats.client.Connection
@@ -11,21 +8,9 @@ import io.nats.client.Message
import io.nats.client.NKey import io.nats.client.NKey
import io.nats.client.Nats import io.nats.client.Nats
import io.nats.client.Options import io.nats.client.Options
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.IOException import java.io.IOException
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.nio.file.Paths
import java.security.GeneralSecurityException import java.security.GeneralSecurityException
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.CertificateFactory
import javax.net.ssl.KeyManager
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.TrustManagerFactory
class NatsManager(datacollector: DashboardActivity) { class NatsManager(datacollector: DashboardActivity) {
@@ -35,57 +20,17 @@ class NatsManager(datacollector: DashboardActivity) {
val datacollector = datacollector val datacollector = datacollector
var connect = false var connect = false
private fun createSSLContext(): SSLContext {
val keyStorePassword = "prime24".toCharArray() // Change as necessary
val clientCertPath = "/storage/sdcard0/Download/client.p12"
// Load client certificate and key
val keyStore = KeyStore.getInstance("PKCS12")
FileInputStream(clientCertPath).use { keyStoreInputStream ->
keyStore.load(keyStoreInputStream, keyStorePassword)
}
val caCertPath = "/storage/sdcard0/Android/data/in.sminnovations.hpostesting.quality/files/NATS/clientCertificate/client-cert.pem"
val caCert = FileInputStream(caCertPath).use { inputStream ->
val certificateFactory = CertificateFactory.getInstance("X.509")
certificateFactory.generateCertificate(inputStream)
}
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
load(null, null) // Initialize the keystore
setCertificateEntry("caCert", caCert) // Add the CA certificate
}
// Initialize key manager factory
val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmf.init(keyStore, keyStorePassword)
// Initialize trust manager factory
val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
tmf.init(trustStore)
// Initialize SSLContext
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(kmf.keyManagers, tmf.trustManagers, SecureRandom())
return sslContext
}
@RequiresApi(Build.VERSION_CODES.O)
fun connect() { fun connect() {
Log.d(TAG, "TRY TO CONNECT") Log.d(TAG, "TRY TO CONNECT")
Thread { Thread {
val seedString = "SUAJH5VMO6GDRQA6QTXCLIJMS74IWIUTU3NJVYIOTZF2LBWUPD77DA2ZEA" val seedString = "SUAEB5PUWNS6C2HUV3MFI6HUDEAPGPFPBHMI73NHIQATCDD2BWALVEZXZ4"
Log.e("seedString",seedString)
val seedBytes = seedString.toCharArray() val seedBytes = seedString.toCharArray()
val theNKey = NKey.fromSeed(seedBytes) // really should load from somewhere val theNKey = NKey.fromSeed(seedBytes) // really should load from somewhere
val options = Options.Builder() val options = Options.Builder()
.server("nats://nanodgx.in:4222") .server("nats://192.168.10.117:4222")
.sslContext(createSSLContext())
.authHandler(object : AuthHandler { .authHandler(object : AuthHandler {
override fun getID(): CharArray? { override fun getID(): CharArray? {
return try { return try {
@@ -123,11 +68,6 @@ class NatsManager(datacollector: DashboardActivity) {
connect = true connect = true
datacollector.setConnect(true) datacollector.setConnect(true)
if (nc?.status == Connection.Status.CONNECTED) {
Log.e("NATSCONNECTION", "NATS is successfully connected.")
} else {
Log.e("NATSCONNECTION", "NATS is not connected. Current status: ${nc?.status}")
}
nc?.publish( nc?.publish(
"server.hpos.HCV-000-3001.ping", "server.hpos.HCV-000-3001.ping",
@@ -142,7 +82,6 @@ class NatsManager(datacollector: DashboardActivity) {
println("PRITIMOI SARKAR $msg") println("PRITIMOI SARKAR $msg")
} }
d?.subscribe("device.hpos.HCV-000-3001.update") { msg -> d?.subscribe("device.hpos.HCV-000-3001.update") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8) val response = String(msg.data, StandardCharsets.UTF_8)
datacollector. setResponse(response) datacollector. setResponse(response)
@@ -176,7 +115,7 @@ class NatsManager(datacollector: DashboardActivity) {
} catch (exp: Exception) { } catch (exp: Exception) {
println(exp.printStackTrace()) println(exp.printStackTrace())
connect = false connect = false
datacollector.setConnect(false) datacollector.setConnect(true)
} }
}.start() }.start()

View File

@@ -3,19 +3,14 @@ package com.example.hpostesting.presentation.assurance
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.example.hpostesting.presentation.NatsManager
import com.example.hpostesting.presentation.dashboard.IDataCollector
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.databinding.ActivityAssuranceControlsBinding import `in`.sminnovations.hpostesting.databinding.ActivityAssuranceControlsBinding
@AndroidEntryPoint @AndroidEntryPoint
class AssuranceControlsActivity: AppCompatActivity(), IDataCollector { class AssuranceControlsActivity: AppCompatActivity() {
lateinit var binding: ActivityAssuranceControlsBinding lateinit var binding: ActivityAssuranceControlsBinding
lateinit var sharedPreference: SharedPreferences lateinit var sharedPreference: SharedPreferences
lateinit var nats: NatsManager
var responses: String = ""
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -29,21 +24,5 @@ class AssuranceControlsActivity: AppCompatActivity(), IDataCollector {
.replace(binding.fgAssuranceControls.id, AssuranceControlsFragment()) .replace(binding.fgAssuranceControls.id, AssuranceControlsFragment())
.commit() .commit()
} }
// nats = NatsManager(this)
// nats.connect()
// nats.pub("server.hpos.HCV-000-3001.ping", "THIS IS A TEST MSG")
}
override fun setConnect(connect: Boolean) {
if(connect){
Log.i("NATS Connection", connect.toString())
}
}
override fun setResponse(response: String) {
responses = responses+response+"\n"
println(responses)
} }
} }

View File

@@ -43,19 +43,11 @@ import com.google.firebase.perf.ktx.performance
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding
import okhttp3.ResponseBody
import org.json.JSONObject import org.json.JSONObject
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.net.URL
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
@AndroidEntryPoint @AndroidEntryPoint
class HomeFragment : Fragment() { class HomeFragment : Fragment() {
@@ -70,6 +62,7 @@ class HomeFragment : Fragment() {
private val homeViewModel: HemoCubeViewModel by activityViewModels() private val homeViewModel: HemoCubeViewModel by activityViewModels()
private var isTokenAvailable = false private var isTokenAvailable = false
private lateinit var sharedPreference: SharedPreferences private lateinit var sharedPreference: SharedPreferences
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
@@ -210,7 +203,6 @@ class HomeFragment : Fragment() {
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData()) hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
hemoCubeViewModel.uploadLogs() hemoCubeViewModel.uploadLogs()
hemoCubeViewModel.startPeriodicCheckUpdate() hemoCubeViewModel.startPeriodicCheckUpdate()
hemoCubeViewModel.downloadClientCertificate()
} }
} }
} else { } else {
@@ -220,11 +212,6 @@ class HomeFragment : Fragment() {
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
hemoCubeViewModel.loginResponse.observe(viewLifecycleOwner) { response -> hemoCubeViewModel.loginResponse.observe(viewLifecycleOwner) { response ->
when (response) { when (response) {
is Result.Success -> { is Result.Success -> {
@@ -279,41 +266,6 @@ class HomeFragment : Fragment() {
} }
} }
hemoCubeViewModel.downloadcertificate.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
val url = response.data
val fileName = "nats_certificate.zip"
val downloadDirectory = "NATS"
val file = downloadFile(url, requireContext(), fileName, downloadDirectory)
Toast.makeText(requireContext(), "NATS certificate Downloaded", Toast.LENGTH_SHORT).show()
val unzipDirectoryPath = requireContext().getExternalFilesDir(null)?.absolutePath + "/$downloadDirectory"
unzip(file.absolutePath, unzipDirectoryPath)
Toast.makeText(requireContext(), "NATS certificate Extracted", Toast.LENGTH_SHORT).show()
}
is Result.Error -> {
response.exception.let { message ->
Toast.makeText(
activity,
"An error occurred in nats download: $message",
Toast.LENGTH_LONG
)
.show()
}
}
is Result.Loading -> {
}
else -> {}
}
}
hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) { hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) {
when (it) { when (it) {
is Result.Success -> { is Result.Success -> {
@@ -338,11 +290,8 @@ class HomeFragment : Fragment() {
else -> {} else -> {}
} }
} }
} }
private fun isTokenExpired(token: String): Boolean { private fun isTokenExpired(token: String): Boolean {
val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT)) val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT))
@@ -392,60 +341,6 @@ class HomeFragment : Fragment() {
) )
} }
private fun downloadFile(responseBody: ResponseBody, context: Context, fileName: String, downloadDirectory: String): File {
// Ensure the download directory exists
val fileDir = File(context.getExternalFilesDir(null), downloadDirectory)
if (!fileDir.exists()) {
fileDir.mkdirs()
}
val file = File(fileDir, fileName)
Log.d("Download", "Starting download to $file")
responseBody.byteStream().use { inputStream ->
FileOutputStream(file).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
// After download
Log.d("Download", "Download completed to ${file.absolutePath}")
return file
}
private fun unzip(zipFilePath: String, destDirectory: String) {
val destDir = File(destDirectory)
if (!destDir.exists()) {
destDir.mkdir()
}
ZipInputStream(FileInputStream(zipFilePath)).use { zipIn ->
var entry: ZipEntry? = zipIn.nextEntry
while (entry != null) {
val filePath = destDirectory + File.separator + entry.name
if (!entry.isDirectory) {
extractFile(zipIn, filePath)
} else {
val dir = File(filePath)
dir.mkdir()
}
zipIn.closeEntry()
entry = zipIn.nextEntry
}
}
}
private fun extractFile(zipIn: ZipInputStream, filePath: String) {
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
val bytesIn = ByteArray(4096)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
}
}
private fun setUserId() { private fun setUserId() {
binding.btnSubmit.setOnClickListener { binding.btnSubmit.setOnClickListener {
val userId = binding.userId.text.toString() val userId = binding.userId.text.toString()

View File

@@ -4,7 +4,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -75,11 +74,6 @@ class DeviceProvisionFragment : Fragment() {
Constants.NATS_TOKEN, Constants.NATS_TOKEN,
response.data.data?.device?.deviceUser?.natsToken response.data.data?.device?.deviceUser?.natsToken
) )
response.data.data?.device?.deviceUser?.natsToken?.let {
Log.e("natstoken",
it
)
}
putString( putString(
Constants.NATS_TOKEN_EXPIRE_DATE, Constants.NATS_TOKEN_EXPIRE_DATE,
response.data.data?.device?.deviceUser?.natsTokenExpiry response.data.data?.device?.deviceUser?.natsTokenExpiry

View File

@@ -153,7 +153,7 @@ class HemoCubeFragment : Fragment() {
when (it) { when (it) {
is Result.Success -> { is Result.Success -> {
uploadedToMolbio = true uploadedToMolbio = true
if (Constants.MOLBIO_INTERGATION) { if (Constants.MOLBIO_INTEGRATION) {
it.data.data?.get(0)?.rawData?.let { it1 -> it.data.data?.get(0)?.rawData?.let { it1 ->
hemoCubeViewModel.updateMolbioFlag( hemoCubeViewModel.updateMolbioFlag(
it1._id it1._id
@@ -805,8 +805,8 @@ class HemoCubeFragment : Fragment() {
calculatedPredictedDenovixRatio = fittedAbs3.div(fittedAbs1) calculatedPredictedDenovixRatio = fittedAbs3.div(fittedAbs1)
val slope = (led1Average - led2Average) / (435 - 415) val slope = (led4Average - led1Average) / (431-411)
val calculatedSlopeRatio = abs(led3Average / slope) val calculatedSlopeRatio = abs(led2Average / slope)
val slopeClass = slopeRatioClassification(calculatedSlopeRatio) val slopeClass = slopeRatioClassification(calculatedSlopeRatio)
if (fittedAbs1 <= fittedAbs2) { if (fittedAbs1 <= fittedAbs2) {
@@ -874,8 +874,8 @@ class HemoCubeFragment : Fragment() {
this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio) this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio)
this.deviceRatioClass = deviceRatioClassification(deviceRatio) this.deviceRatioClass = deviceRatioClassification(deviceRatio)
this.slopeRatioClass = slopeClass this.slopeRatioClass = slopeClass
this.classificationResult = deviceRatioClass //findResult(calculatedRatio) this.classificationResult = findResultWithAdditionalMethods(deviceRatio, deviceRatioClass, slopeRatio)
hemoCubeViewModel.messages.postValue("${this.deviceRatioClass} ${if (led4Average < 0.17) " - Low Hb" else ""}\n") hemoCubeViewModel.messages.postValue("${this.classificationResult} ")
if (DataHolder.hemoCubeTestData?.testType == "HB") if (DataHolder.hemoCubeTestData?.testType == "HB")
hemoCubeViewModel.messages.postValue("Hb: $calculatedHb4") hemoCubeViewModel.messages.postValue("Hb: $calculatedHb4")
this.errorMessages = testState.allErrorMessages this.errorMessages = testState.allErrorMessages
@@ -909,50 +909,36 @@ class HemoCubeFragment : Fragment() {
} }
} }
fun findResult(calculatedRatio: Double?): String { fun findResultWithAdditionalMethods(deviceRatio: Double?, deviceRatioClass: String?, slopeRatio: Double?): String {
try { try {
hemoCubeViewModel.messages.postValue("result classification") // hemoCubeViewModel.messages.postValue("post classification checks")
if (calculatedRatio != null) { if (deviceRatio != null) {
if (calculatedRatio < 0.05) if (slopeRatio != null) {
return getString(R.string.error_repeat_test_higher_volume) if (deviceRatioClass == "Normal" && slopeRatio > 60.0)
if (calculatedRatio in 0.05..0.155) { return "Negative Borderline, Repeat Test"
return getString(R.string.normal)
} }
if (calculatedRatio in 0.155..0.175)
return getString(R.string.negative_borderline)
if (calculatedRatio in 0.175..0.22)
return getString(R.string.sickle_cell_trait)
if (calculatedRatio in 0.22..0.25)
return getString(R.string.positive_for_sickle_cell)
if (calculatedRatio in 0.25..0.35)
return getString(R.string.sickle_cell_disease)
if (calculatedRatio > 0.35)
return getString(R.string.error_repeat_test_lower_volume)
} else {
return getString(R.string.invalid)
} }
} catch (e: Exception) { } catch (e: Exception) {
showToast(R.string.error_classification) handleException(e)
Firebase.crashlytics.recordException(e) return "Error"
return getString(R.string.error)
} }
return getString(R.string.invalid) return deviceRatioClass.toString()
} }
fun deviceRatioClassification(ratio: Double?): String { fun deviceRatioClassification(ratio: Double?): String {
try { try {
if (ratio != null) { if (ratio != null) {
if (ratio in 0.1..0.29) { if (ratio in 0.001..0.23) {
// setSubtitleTextColor(R.color.green_2) // setSubtitleTextColor(R.color.green_2)
return "Normal" return "Normal"
} }
if (ratio in 0.29..0.32) if (ratio in 0.23..0.24)
return "Negative Borderline, Repeat Test" return "Negative Borderline, Repeat Test"
if (ratio in 0.32..0.35) if (ratio in 0.24..0.29)
return "Sickle Cell Trait" return "Sickle Cell Trait"
if (ratio in 0.35..0.38) if (ratio in 0.29..0.32)
return "Positive for Sickle Cell. HPLC for Confirmation" return "Positive for Sickle Cell. HPLC for Confirmation"
if (ratio in 0.38..0.5) if (ratio in 0.32..Double.POSITIVE_INFINITY)
return "Sickle Cell Disease" return "Sickle Cell Disease"
} else { } else {
return "Invalid" return "Invalid"

View File

@@ -79,7 +79,6 @@ class HemoCubeViewModel @Inject constructor(
val checkUpdate = MutableLiveData<Result<CheckUpdateResponse>>() val checkUpdate = MutableLiveData<Result<CheckUpdateResponse>>()
val deviceUpdate = MutableLiveData<Result<ResponseBody>>() val deviceUpdate = MutableLiveData<Result<ResponseBody>>()
val downloadcertificate = MutableLiveData<Result<ResponseBody>>()
val uploadLogs = MutableLiveData<Result<UploadLogsResponse>?>() val uploadLogs = MutableLiveData<Result<UploadLogsResponse>?>()
@@ -142,9 +141,6 @@ class HemoCubeViewModel @Inject constructor(
} }
} }
fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch { fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch {
checkUpdate.postValue(Result.Loading()) checkUpdate.postValue(Result.Loading())
repository.checkUpdate(checkUpdateRequest).let { repository.checkUpdate(checkUpdateRequest).let {
@@ -159,17 +155,6 @@ class HemoCubeViewModel @Inject constructor(
} }
} }
fun downloadClientCertificate() = viewModelScope.launch {
downloadcertificate.postValue(Result.Loading())
repository.downloadClientCertificate().let {
downloadcertificate.postValue(it)
}
}
fun startPeriodicCheckUpdate() { fun startPeriodicCheckUpdate() {
val periodicRequest = PeriodicWorkRequestBuilder<CheckUpdateWorker>( val periodicRequest = PeriodicWorkRequestBuilder<CheckUpdateWorker>(
repeatInterval = 1, repeatIntervalTimeUnit = TimeUnit.MINUTES repeatInterval = 1, repeatIntervalTimeUnit = TimeUnit.MINUTES

View File

@@ -156,7 +156,7 @@ class TrueHemeFragment : Fragment() {
when (it) { when (it) {
is Result.Success -> { is Result.Success -> {
uploadedToMolbio = true uploadedToMolbio = true
if (Constants.MOLBIO_INTERGATION) { if (Constants.MOLBIO_INTEGRATION) {
it.data.data?.get(0)?.rawData?.let { it1 -> it.data.data?.get(0)?.rawData?.let { it1 ->
hemoCubeViewModel.updateMolbioFlag( hemoCubeViewModel.updateMolbioFlag(
it1._id it1._id

View File

@@ -318,28 +318,28 @@ class HemoCubeFragmentTest {
@Test @Test
fun testDeviceRatioClassificationNormal() { fun testDeviceRatioClassificationNormal() {
val ratio = 0.25 val ratio = 0.22
val result = hemoCubeFragment.deviceRatioClassification(ratio) val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Normal", result) assertEquals("Normal", result)
} }
@Test @Test
fun testDeviceRatioClassificationNegativeBorderline() { fun testDeviceRatioClassificationNegativeBorderline() {
val ratio = 0.31 val ratio = 0.235
val result = hemoCubeFragment.deviceRatioClassification(ratio) val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Negative Borderline, Repeat Test", result) assertEquals("Negative Borderline, Repeat Test", result)
} }
@Test @Test
fun testDeviceRatioClassificationSickleCellTrait() { fun testDeviceRatioClassificationSickleCellTrait() {
val ratio = 0.34 val ratio = 0.25
val result = hemoCubeFragment.deviceRatioClassification(ratio) val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Sickle Cell Trait", result) assertEquals("Sickle Cell Trait", result)
} }
@Test @Test
fun testDeviceRatioClassificationPositiveForSickleCell() { fun testDeviceRatioClassificationPositiveForSickleCell() {
val ratio = 0.37 val ratio = 0.31
val result = hemoCubeFragment.deviceRatioClassification(ratio) val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Positive for Sickle Cell. HPLC for Confirmation", result) assertEquals("Positive for Sickle Cell. HPLC for Confirmation", result)
} }
@@ -357,4 +357,18 @@ class HemoCubeFragmentTest {
val result = hemoCubeFragment.deviceRatioClassification(ratio) val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Invalid", result) assertEquals("Invalid", result)
} }
@Test
fun findResultWithAdditionalMethods_ValidInput_ReturnsNegativeBorderlineRepeatTest() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Normal", 70.0)).thenReturn("Negative Borderline, Repeat Test")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Normal", 70.0)
assertEquals("Negative Borderline, Repeat Test", result)
}
@Test
fun findResultWithAdditionalMethods_ValidInput_ReturnsDeviceRatioClassToString() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)
assertEquals("Abnormal", result)
}
} }