diff --git a/app/build.gradle b/app/build.gradle index 4fae05b..61967bf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,8 +19,8 @@ android { applicationId "in.sminnovations.hpostesting" minSdk 21 targetSdk 34 - versionCode 28 - versionName "2.1.28" + versionCode 30 + versionName "2.1.30" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -80,6 +80,12 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + // Mockito dependencies + testImplementation 'org.mockito:mockito-core:3.12.4' + androidTestImplementation 'org.mockito:mockito-android:3.12.4' + androidTestImplementation 'org.mockito:mockito-inline:3.12.4' + testImplementation "androidx.arch.core:core-testing:2.2.0" + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4' implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 98c18c5..c2f6365 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,9 @@ + + diff --git a/app/src/main/java/com/example/hpostesting/data/constant/Constants.kt b/app/src/main/java/com/example/hpostesting/data/constant/Constants.kt index 33aff22..3212e10 100644 --- a/app/src/main/java/com/example/hpostesting/data/constant/Constants.kt +++ b/app/src/main/java/com/example/hpostesting/data/constant/Constants.kt @@ -75,6 +75,39 @@ object Constants { const val BUFFER_VALUE_4 = "BufferValue4" const val DEVICE_ID = "DEVICE_ID" - const val BUFFER_LED_LOWER_BOUND = 20000 - const val BUFFER_LED_HIGHER_BOUND = 24000 + const val BUFFER_LED_LOWER_BOUND = 21000 + const val BUFFER_LED_UPPER_BOUND = 24500 + + val DEVICE_CONFIGURATION: Map>> = mapOf>>( + "HCV-000-3001" to listOf( + listOf(1.264817, -0.85965), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.184991, 0.001704), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3002" to listOf( + listOf(1.264817, -0.85965), + listOf(0.581462456, -0.58502), + listOf(0.184991, 0.001704), + listOf(1.0, 0.0) + ), + ) + + const val INCUBATION_TIME_MIN = 15 + const val INCUBATION_TIME_MAX = 30 + + val BUFFER_INTENSITY_THRESHOLDS: Map>> = mapOf>>( + "HCV-000-3001" to listOf( + listOf(24000, 24500), + listOf(21000, 24500), + listOf(21000, 24500), + listOf(21000, 24500), + ), + "HCV-000-3002" to listOf( + listOf(21000, 24500), + listOf(21000, 24500), + listOf(21000, 24500), + listOf(21000, 24500), + ) + ) } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt b/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt index 339d58d..62cbda1 100644 --- a/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt +++ b/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt @@ -11,7 +11,7 @@ import com.example.hpostesting.data.model.patient.HemoCubeTestData import com.google.android.datatransport.runtime.dagger.Provides import javax.inject.Singleton -@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class], version = 5, exportSchema = false) +@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class], version = 10, exportSchema = false) @TypeConverters(Converters::class) abstract class MyDatabase : RoomDatabase() { abstract fun userDao(): UserDao diff --git a/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt b/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt index 8f3b663..f410705 100644 --- a/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt +++ b/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt @@ -8,6 +8,8 @@ data class HemoCubeTestData( @PrimaryKey var _id: String = "", var name: String = "", + var incubationTime: String = "", + var bloodGroup: String = "", var birthYear: String = "", var userImageURL: String = "", var location: UserData.Location? = null, @@ -34,8 +36,19 @@ data class HemoCubeTestData( var led2Average: Double? = null, var led3Average: Double? = null, var led4Average: Double? = null, + var abs1: Double? = null, + var abs2: Double? = null, + var abs3: Double? = null, + var abs4: Double? = null, var deviceRatio: Double? = null, var calculatedRatio: Double? = null, + var predictedDenovixRatio: Double? = null, var coefficients: String? = "", - var classificationResult: String = "" + var classificationResult: String = "", + var prdClassification: String = "", + var batteryLevel: String = "", + var batteryCapacity: String = "", + var batteryMaxCapacity: String = "", + var batteryTemperature: String = "", + var batteryVoltage: String = "", ) diff --git a/app/src/main/java/com/example/hpostesting/data/model/patient/UserData.kt b/app/src/main/java/com/example/hpostesting/data/model/patient/UserData.kt index 1b6c95d..a7ad7fd 100644 --- a/app/src/main/java/com/example/hpostesting/data/model/patient/UserData.kt +++ b/app/src/main/java/com/example/hpostesting/data/model/patient/UserData.kt @@ -9,6 +9,7 @@ data class UserData( @PrimaryKey var _id: String = "", var name: String = "", + var bloodGroup: String = "", var incubationTime: String = "", var birthYear: String = "", var userImageURL: String = "", @@ -44,5 +45,7 @@ fun UserData.toHemoCubeTestData() = HemoCubeTestData( birthYear = birthYear, userImageURL = userImageURL, testStatus = testStatus, + bloodGroup = bloodGroup, + incubationTime = incubationTime, location = location ) diff --git a/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt b/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt index a7a2d8f..728aa3d 100644 --- a/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt +++ b/app/src/main/java/com/example/hpostesting/data/repository/DatabaseRepository.kt @@ -15,13 +15,14 @@ import com.google.firebase.ktx.Firebase import com.google.firebase.storage.ktx.storage import kotlinx.coroutines.tasks.await import java.io.File +import javax.inject.Inject -class DatabaseRepository : Repository { +class DatabaseRepository @Inject constructor() : Repository { private val db: FirebaseFirestore = Firebase.firestore private val storage = Firebase.storage - suspend fun addTestToDatabase(data: HemoCubeTestData?): Response { + override suspend fun addTestToDatabase(data: HemoCubeTestData?): Response { return try { val userdata = db.collection("patientData").whereEqualTo("_id", data!!._id).get().await() if (userdata.documents.isNotEmpty()) { @@ -36,7 +37,7 @@ class DatabaseRepository : Repository { } } - suspend fun addTestToDatabase(data: UserData?): Response { + override suspend fun addTestToDatabase(data: UserData?): Response { return try { val userdata = db.collection("patientData").whereEqualTo("_id", data!!._id).get().await() if (userdata.documents.isNotEmpty()) { @@ -52,7 +53,7 @@ class DatabaseRepository : Repository { } } - suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response { + override suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response { return try { db.collection("buffers").add(data!!).await() Response.Success(data!!.kitno) @@ -62,7 +63,7 @@ class DatabaseRepository : Repository { } } - suspend fun addDiagnostics(data: DiagnosticsData?): Response { + override suspend fun addDiagnostics(data: DiagnosticsData?): Response { return try { db.collection("diagnostics").add(data!!).await() Response.Success(data!!.deviceId) @@ -72,7 +73,7 @@ class DatabaseRepository : Repository { } } - suspend fun uploadFileToStorage(patientID: String, filePath: String): Response { + override suspend fun uploadFileToStorage(patientID: String, filePath: String): Response { try { val file = Uri.fromFile(File(filePath)) @@ -131,7 +132,7 @@ class DatabaseRepository : Repository { return db.collection("devices").get().await().toObjects(DeviceData::class.java) } - suspend fun getDeviceDataById(deviceId: String): DeviceData? { + override suspend fun getDeviceDataById(deviceId: String): DeviceData? { val querySnapshot = db.collection("devices").get().await() val allDeviceDataList = querySnapshot.toObjects(DeviceData::class.java) diff --git a/app/src/main/java/com/example/hpostesting/data/repository/Repository.kt b/app/src/main/java/com/example/hpostesting/data/repository/Repository.kt index 3419ceb..f809295 100644 --- a/app/src/main/java/com/example/hpostesting/data/repository/Repository.kt +++ b/app/src/main/java/com/example/hpostesting/data/repository/Repository.kt @@ -1,5 +1,22 @@ package com.example.hpostesting.data.repository +import com.example.hpostesting.data.model.Response +import com.example.hpostesting.data.model.diagnostics.DiagnosticsData +import com.example.hpostesting.data.model.patient.BufferCheckData +import com.example.hpostesting.data.model.patient.DeviceData +import com.example.hpostesting.data.model.patient.HemoCubeTestData +import com.example.hpostesting.data.model.patient.UserData + interface Repository { -// suspend fun addToDatabase(data: PatientDetails) + suspend fun addTestToDatabase(data: HemoCubeTestData?): Response + + suspend fun addTestToDatabase(data: UserData?): Response + + suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response + + suspend fun addDiagnostics(data: DiagnosticsData?): Response + + suspend fun uploadFileToStorage(patientID: String, filePath: String): Response + + suspend fun getDeviceDataById(deviceId: String): DeviceData? } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt b/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt index e0252b6..3c4b5ce 100644 --- a/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt +++ b/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt @@ -9,6 +9,7 @@ import com.example.hpostesting.data.dao.UserDao import com.example.hpostesting.data.datasource.LocalFileDataSource import com.example.hpostesting.data.repository.DatabaseRepository import com.example.hpostesting.data.repository.LocalFileRepository +import com.example.hpostesting.data.repository.Repository import com.example.hpostesting.domain.SaveRawData import com.example.hpostesting.domain.SaveRawDataTest import dagger.Module @@ -69,4 +70,10 @@ object AppModule { fun provideDatabaseRepository(): DatabaseRepository { return DatabaseRepository() } + + @Provides + @Singleton + fun provideRepository(): Repository { + return DatabaseRepository() + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/adapter/OfflineUserListAdapter.kt b/app/src/main/java/com/example/hpostesting/presentation/adapter/OfflineUserListAdapter.kt new file mode 100644 index 0000000..6acd933 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/adapter/OfflineUserListAdapter.kt @@ -0,0 +1,122 @@ +package com.example.hpostesting.presentation.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.navigation.findNavController +import androidx.recyclerview.widget.AsyncListDiffer +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.example.hpostesting.data.DataHolder +import com.example.hpostesting.data.constant.Constants +import com.example.hpostesting.data.model.patient.HemoCubeTestData +import com.example.hpostesting.data.model.patient.UserData +import `in`.sminnovations.hpostesting.R +import `in`.sminnovations.hpostesting.databinding.OfflineUserListViewBinding +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date +import java.util.Locale + +class OfflineUserListAdapter(private val view: View, private val batLevel: Int) : + RecyclerView.Adapter() { + + inner class OfflineUserListViewHolder(val binding: OfflineUserListViewBinding) : + RecyclerView.ViewHolder(binding.root) + + private val differCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldItem: HemoCubeTestData, newItem: HemoCubeTestData + ): Boolean { + return oldItem._id == newItem._id + } + + override fun areContentsTheSame( + oldItem: HemoCubeTestData, newItem: HemoCubeTestData + ): Boolean { + return oldItem == newItem + } + } + + val differ = AsyncListDiffer(this, differCallback) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OfflineUserListViewHolder { + return OfflineUserListViewHolder( + OfflineUserListViewBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + } + + override fun getItemCount(): Int { + return differ.currentList.size + } + + override fun onBindViewHolder(holder: OfflineUserListViewHolder, position: Int) { + val userList = differ.currentList[position] + holder.binding.apply { + userID.text = "User ID: ${userList._id}" + bloodGroup.text = "Blood group: ${userList.bloodGroup}" + time.text = "Time: ${userList.incubationTime}" + userCard.setOnClickListener { + if (batLevel < 40) { + Toast.makeText( + view.context, + "Battery level is low than 40%, please charge the device to continue testing", + Toast.LENGTH_SHORT + ).show() + return@setOnClickListener + } + if (userList.testStatus != null) { + if (userList.testStatus!!) { + Toast.makeText( + view.context, + "Test has been already conducted for this user", + Toast.LENGTH_SHORT + ).show() + } else { + if (userList.incubationTime != "") { + if (isBetween15And30Minutes(userList.incubationTime) < Constants.INCUBATION_TIME_MIN) { + Toast.makeText( + view.context, + "Incubation has not completed 15 minutes", + Toast.LENGTH_SHORT + ).show() + } else if (isBetween15And30Minutes(userList.incubationTime) > Constants.INCUBATION_TIME_MAX) { + Toast.makeText( + view.context, + "Incubation crossed 30 minutes, need to repeat the incubation", + Toast.LENGTH_SHORT + ).show() + } else { + DataHolder.selectedTest = UserData( + _id = userList._id, + bloodGroup = userList.bloodGroup, + incubationTime = userList.incubationTime + ) + view.findNavController() + .navigate(R.id.action_nav_home_to_mainActivity) + } + } else { + Toast.makeText( + view.context, + "Incubation not started for this user", + Toast.LENGTH_SHORT + ).show() + } + } + } + } + } + } + + private fun isBetween15And30Minutes(createdAt: String): Long { + val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + val createdAtDate: Date = formatter.parse(createdAt)!! + + val currentTime = Calendar.getInstance().time + + val diffMillis = currentTime.time - createdAtDate.time + + return diffMillis / (60 * 1000) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt index 1a6995f..93f7414 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt @@ -20,6 +20,7 @@ import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.model.patient.HemoCubeTestData import com.example.hpostesting.data.model.patient.UserData import com.example.hpostesting.presentation.KitScanActivity +import com.example.hpostesting.presentation.adapter.OfflineUserListAdapter import com.example.hpostesting.presentation.adapter.UserListAdapter import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel import com.example.hpostesting.presentation.testRight.TestRightViewModel @@ -33,6 +34,7 @@ import dagger.hilt.android.AndroidEntryPoint import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding import java.text.SimpleDateFormat +import java.util.Calendar import java.util.Date import java.util.Locale @@ -43,7 +45,9 @@ class HomeFragment : Fragment() { private val viewModel: TestRightViewModel by activityViewModels() private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels() private lateinit var rvAdapter: UserListAdapter - private var batLevel: Int = 0 // Initialize with a default value, or obtain the actual battery level + private var batLevel: Int = + 0 // Initialize with a default value, or obtain the actual battery level + private lateinit var adapter: OfflineUserListAdapter private lateinit var sharedPreference: SharedPreferences @@ -73,6 +77,19 @@ class HomeFragment : Fragment() { } hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userData -> deleteHemoCubeIncompleteRegistrations(userData) + if (userData.isNotEmpty()) { + val userList = mutableListOf() + userData.forEach { + if (it.testStatus == false) { + userList.add(it) + } + } + val bm = requireContext().getSystemService(BATTERY_SERVICE) as BatteryManager + val batLevel: Int = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) + adapter = OfflineUserListAdapter(binding.root, batLevel) + adapter.differ.submitList(userList) + binding.rvOrderOffline.adapter = adapter + } } viewModel.networkStatusLiveData.observe(viewLifecycleOwner) { isConnected -> if (isConnected) { @@ -120,10 +137,18 @@ class HomeFragment : Fragment() { private fun setUserId() { binding.btnSubmit.setOnClickListener { val userId = binding.userId.text.toString() - if (userId.length >= 18) { - val userData = UserData(_id = userId) - DataHolder.selectedTest = userData - findNavController().navigate(R.id.action_nav_home_to_mainActivity) + val bloodGroup = binding.etBloodGroup.text + if (userId.length >= 18 && !bloodGroup.equals("Select Blood Group") || !bloodGroup.isNullOrBlank()) { + hemoCubeViewModel.addUser( + HemoCubeTestData( + _id = userId, bloodGroup = bloodGroup.toString(), incubationTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time).toString() + ) + ) +// val userData = UserData(_id = userId) +// DataHolder.selectedTest = userData +// findNavController().navigate(R.id.action_nav_home_to_mainActivity) } else { val errorMessage = getString(R.string.user_id_error_message) Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show() @@ -155,10 +180,12 @@ class HomeFragment : Fragment() { val bm = requireContext().getSystemService(BATTERY_SERVICE) as BatteryManager val batLevel: Int = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) - rvAdapter = view?.let { - UserListAdapter(requireContext(), hemoCubeViewModel, recyclerViewOptions, - it, batLevel, requireActivity()) - }!! + rvAdapter = view?.let { + UserListAdapter( + requireContext(), hemoCubeViewModel, recyclerViewOptions, + it, batLevel, requireActivity() + ) + }!! binding.rvOrder.adapter = rvAdapter rvAdapter.startListening() @@ -206,8 +233,9 @@ class HomeFragment : Fragment() { val partition = dateFormat.format(currentDate) val searchTerm = partition + search val searchField = field + "Search" - val query = Firebase.firestore.collection("patientData").orderBy(searchField).startAt(searchTerm) - .endAt(searchTerm + "\uf8ff") + val query = + Firebase.firestore.collection("patientData").orderBy(searchField).startAt(searchTerm) + .endAt(searchTerm + "\uf8ff") query.get().addOnSuccessListener { userSearchTrace.stop() } @@ -218,7 +246,14 @@ class HomeFragment : Fragment() { batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) rvAdapter = view?.let { - UserListAdapter(requireContext(), hemoCubeViewModel, recyclerViewOptions, it, batLevel, requireActivity()) + UserListAdapter( + requireContext(), + hemoCubeViewModel, + recyclerViewOptions, + it, + batLevel, + requireActivity() + ) }!! binding.rvOrder.adapter = rvAdapter rvAdapter.startListening() @@ -272,7 +307,8 @@ class HomeFragment : Fragment() { } hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> - val uploadDataVisibility = if (userDataList.any { !it.localFlag }) View.VISIBLE else View.GONE + val uploadDataVisibility = + if (userDataList.any { !it.localFlag && it.testStatus == true}) View.VISIBLE else View.GONE binding.uploadData.visibility = uploadDataVisibility } } @@ -331,7 +367,7 @@ class HomeFragment : Fragment() { private fun deleteHemoCubeIncompleteRegistrations(userDataList: List) { userDataList.forEach { userData -> - if (userData.testTime?.isEmpty() == true) { + if (userData._id.isEmpty()) { hemoCubeViewModel.deleteById(userData._id) } } diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt index 735fcc6..d392a37 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.content.SharedPreferences import android.graphics.Color import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -20,7 +19,6 @@ import com.example.hpostesting.data.model.patient.DeviceData import com.example.hpostesting.data.model.patient.toHemoCubeTestData import com.example.hpostesting.presentation.UsbServiceListener import com.example.hpostesting.presentation.dashboard.DashboardActivity -import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity import com.example.hpostesting.presentation.utils.MyDialogListener import com.example.hpostesting.presentation.utils.UIUtils import com.google.firebase.crashlytics.ktx.crashlytics @@ -35,7 +33,7 @@ class HemoCubeFragment : Fragment() { private lateinit var binding: FragmentHemoCubeReferenceBinding private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels() private lateinit var sharedPreferences: SharedPreferences - val testDetails = DataHolder.selectedTest?.toHemoCubeTestData() + private val testDetails = DataHolder.selectedTest?.toHemoCubeTestData() private var isOnline = false private var currentDeviceData: DeviceData? = null private var resultData: String = "" @@ -51,7 +49,13 @@ class HemoCubeFragment : Fragment() { private var led2SampleForDevice = 0.0 private var led3SampleForDevice = 0.0 private var led4SampleForDevice = 0.0 + private var fittedAbs1 = 0.0 + private var fittedAbs2 = 0.0 + private var fittedAbs3 = 0.0 + private var fittedAbs4 = 0.0 + private var _predictedDenovixRatio = 0.0 private var validationError = false + private var deviceHardwareId = "" override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, @@ -95,7 +99,7 @@ class HemoCubeFragment : Fragment() { binding.btnSamplestart.setOnClickListener { activity?.runOnUiThread { binding.tvSubtitle4.visibility = View.VISIBLE - binding.tvSubtitle4.text = "Sample Started" +// binding.tvSubtitle4.text = "Sample Started" } startSampleProcess() it.visibility = View.GONE @@ -287,6 +291,7 @@ class HemoCubeFragment : Fragment() { val slData = stringData.split(" ") if (slData.size > 1) { val hardwareId = slData[1].trim() + deviceHardwareId = hardwareId with(sharedPreferences.edit()) { putString(Constants.DEVICE_ID, hardwareId) apply() @@ -317,12 +322,16 @@ class HemoCubeFragment : Fragment() { } stringData.contains("#SC") -> { - binding.tvSubtitle4.text = "Sample Completed" + hemoCubeViewModel.messages.postValue("Sample Completed \nGathering data") fetchResult() testingTrace.stop() } resultData.contains("REND") -> { + hemoCubeViewModel.messages.postValue( + "Data collected \n" + + " Processing data" + ) val resultLines = resultData.split("\\s+(?=LB|LS)".toRegex()) var bufferIntensity = resultLines[1].split(' ')[1].trim() led1BufferForDevice = if (isUsingExistingBuffer) { @@ -408,7 +417,58 @@ class HemoCubeFragment : Fragment() { val led4Average = log10(led4BufferForDevice?.div(led4SampleForDevice!!) ?: 0.0) val deviceRatio = led3Average / led1Average - if (led1Average < 0 || led2Average < 0 || led3Average < 0 || led4Average < 0) { + if (led1BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0)?.get(0)!! + || led2BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(1)?.get(0)!! + || led3BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(2)?.get(0)!! + || led4BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(3)?.get(0)!! + ) { + validationError = true + activity?.runOnUiThread { + binding.errorMessage.text = "Error: Invalid Test. Improper buffer reading (low)" + binding.errorMessage.visibility = View.VISIBLE + } + } + + if (led1BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0)?.get(1)!! + || led2BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(1)?.get(1)!! + || led3BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(2)?.get(1)!! + || led4BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(3)?.get(1)!! + ) { + validationError = true + activity?.runOnUiThread { + binding.errorMessage.text = + "Error: Invalid Test. Improper buffer reading (high)" + binding.errorMessage.visibility = View.VISIBLE + } + } + + var gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(0) + var constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(1) + fittedAbs1 = gradient?.times(led1Average)?.plus(constant!!)!! + + gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(1)?.get(0) + constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(1)?.get(1) + fittedAbs2 = gradient?.times(led2Average)?.plus(constant!!)!! + + gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(2)?.get(0) + constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(2)?.get(1) + fittedAbs3 = gradient?.times(led3Average)?.plus(constant!!)!! + + gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(3)?.get(0) + constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(3)?.get(1) + fittedAbs4 = gradient?.times(led4Average)?.plus(constant!!)!! + + _predictedDenovixRatio = fittedAbs3?.div(fittedAbs1!!)!! + + if (fittedAbs1!! <= fittedAbs2!!) { + validationError = true + activity?.runOnUiThread { + binding.errorMessage.text = "Error: Invalid Test. Problem with de-oxygenation" + binding.errorMessage.visibility = View.VISIBLE + } + } + + if (fittedAbs1 < 0 || fittedAbs2 < 0 || fittedAbs3 < 0 || fittedAbs4 < 0) { validationError = true activity?.runOnUiThread { binding.errorMessage.text = "Error: Negative Abs. Retake Blank Reading" @@ -416,30 +476,6 @@ class HemoCubeFragment : Fragment() { } } - if (led1BufferForDevice < Constants.BUFFER_LED_LOWER_BOUND - || led2BufferForDevice < Constants.BUFFER_LED_LOWER_BOUND - || led3BufferForDevice < Constants.BUFFER_LED_LOWER_BOUND - || led4BufferForDevice < Constants.BUFFER_LED_LOWER_BOUND - ) { - validationError = true - activity?.runOnUiThread { - binding.errorMessage.text = "Error: Invalid Test. Blank reading is too low" - binding.errorMessage.visibility = View.VISIBLE - } - } - - if (led1BufferForDevice > Constants.BUFFER_LED_HIGHER_BOUND - || led2BufferForDevice > Constants.BUFFER_LED_HIGHER_BOUND - || led3BufferForDevice > Constants.BUFFER_LED_HIGHER_BOUND - || led4BufferForDevice > Constants.BUFFER_LED_HIGHER_BOUND - ) { - validationError = true - activity?.runOnUiThread { - binding.errorMessage.text = "Error: Invalid Test. Blank reading is too high" - binding.errorMessage.visibility = View.VISIBLE - } - } - DataHolder.hemoCubeTestData?.apply { deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString() led1Buffer = led1BufferForDevice @@ -455,29 +491,42 @@ class HemoCubeFragment : Fragment() { this.led2Average = led2Average this.led3Average = led3Average this.led4Average = led4Average + this.abs1 = fittedAbs1 + this.abs2 = fittedAbs2 + this.abs3 = fittedAbs3 + this.abs4 = fittedAbs4 this.deviceRatio = deviceRatio this.calculatedRatio = calculateRatio(deviceRatio) + this.predictedDenovixRatio = _predictedDenovixRatio this.coefficients = currentDeviceData?.coefficients?.get(0) .toString() + ", " + currentDeviceData?.coefficients?.get(1).toString() this.classificationResult = findResult(calculatedRatio) - hemoCubeViewModel.messages.postValue(this.classificationResult) + this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio) + hemoCubeViewModel.messages.postValue(this.prdClassification) this.resultData = deviceLog - if (!isUsingExistingBuffer) { - with(sharedPreferences.edit()) { - putString(Constants.BUFFER_VALUE_1, led1BufferForDevice.toString()) - putString(Constants.BUFFER_VALUE_2, led2BufferForDevice.toString()) - putString(Constants.BUFFER_VALUE_3, led3BufferForDevice.toString()) - putString(Constants.BUFFER_VALUE_4, led4BufferForDevice.toString()) - apply() - } + this.batteryLevel = hemoCubeViewModel.getBatteryLevel().toString() + this.batteryCapacity = hemoCubeViewModel.getBatteryCapacity(requireContext()).toString() + this.batteryMaxCapacity = hemoCubeViewModel.getBatteryMaxCapacity(requireContext()).toString() + this.batteryTemperature = hemoCubeViewModel.getBatteryTemperature().toString() + this.batteryVoltage = hemoCubeViewModel.getBatteryVoltage(requireContext()).toString() + } + + if (!isUsingExistingBuffer) { + with(sharedPreferences.edit()) { + putString(Constants.BUFFER_VALUE_1, led1BufferForDevice.toString()) + putString(Constants.BUFFER_VALUE_2, led2BufferForDevice.toString()) + putString(Constants.BUFFER_VALUE_3, led3BufferForDevice.toString()) + putString(Constants.BUFFER_VALUE_4, led4BufferForDevice.toString()) + apply() } - if (!validationError) { - activity?.runOnUiThread { - binding.btnSubmit.visibility = View.VISIBLE - binding.btnSubmit.isEnabled = true - binding.btnSubmit.isClickable = true - binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) - } + } + + if (!validationError) { + activity?.runOnUiThread { + binding.btnSubmit.visibility = View.VISIBLE + binding.btnSubmit.isEnabled = true + binding.btnSubmit.isClickable = true + binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) } } } catch (e: Exception) { @@ -490,7 +539,7 @@ class HemoCubeFragment : Fragment() { } } - private fun findResult(calculatedRatio: Double?): String { + fun findResult(calculatedRatio: Double?): String { try { hemoCubeViewModel.messages.postValue("result classification") if (calculatedRatio != null) { @@ -519,6 +568,31 @@ class HemoCubeFragment : Fragment() { return "INVALID" } + private fun absorbanceBasedClassification(predictedDenovixRatio: Double?): String { + try { + hemoCubeViewModel.messages.postValue("result classification") + if (predictedDenovixRatio != null) { + if (predictedDenovixRatio in 0.0..0.16) + return "Normal" + if (predictedDenovixRatio in 0.16..0.165) + return "Negative Borderline" + if (predictedDenovixRatio in 0.165..0.235) + return "Sickle Cell Trait" + if (predictedDenovixRatio in 0.235..0.24) + return "Positive Borderline" + if (predictedDenovixRatio in 0.24..1.0) + return "Sickle Cell Disease" + } else { + return "INVALID" + } + } catch (e: Exception) { + showToast("error while performing classification") + Firebase.crashlytics.recordException(e) + return "ERROR" + } + return "INVALID" + } + private fun showToast(message: String) { Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt index 4a7dd6b..b4b4305 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt @@ -1,7 +1,10 @@ package com.example.hpostesting.presentation.hemocube import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.SharedPreferences +import android.os.BatteryManager import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -15,8 +18,10 @@ import com.example.hpostesting.data.model.Response import com.example.hpostesting.data.model.patient.BufferCheckData import com.example.hpostesting.data.model.patient.DeviceData import com.example.hpostesting.data.model.patient.HemoCubeTestData +import com.example.hpostesting.data.model.patient.UserData import com.example.hpostesting.data.model.patient.toHemoCubeTestData import com.example.hpostesting.data.repository.DatabaseRepository +import com.example.hpostesting.data.repository.Repository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import java.text.SimpleDateFormat @@ -27,7 +32,7 @@ import javax.inject.Inject @HiltViewModel class HemoCubeViewModel @Inject constructor( private val hemoCubeDao: HemoCubeDao, - private val repository: DatabaseRepository, + private val repository: Repository, context: Context, ) : ViewModel() { var isServiceConnected = false @@ -46,6 +51,11 @@ class HemoCubeViewModel @Inject constructor( get() = _networkStatusLiveData val fireBaseUpload = MutableLiveData() val fireBaseBulkUpload = MutableLiveData() + + private val batteryStatus: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter -> + context.registerReceiver(null, ifilter) + } + fun uploadHemoCubeResultToDatabase(isOnline: Boolean, testStatus: Boolean, kitSerial: String?) = viewModelScope.launch { if (kitSerial != null) { @@ -105,10 +115,21 @@ class HemoCubeViewModel @Inject constructor( testDetails?.led2Average = DataHolder.hemoCubeTestData?.led2Average testDetails?.led3Average = DataHolder.hemoCubeTestData?.led3Average testDetails?.led4Average = DataHolder.hemoCubeTestData?.led4Average + testDetails?.abs1 = DataHolder.hemoCubeTestData?.abs1 + testDetails?.abs2 = DataHolder.hemoCubeTestData?.abs2 + testDetails?.abs3 = DataHolder.hemoCubeTestData?.abs3 + testDetails?.abs4 = DataHolder.hemoCubeTestData?.abs4 testDetails?.deviceRatio = DataHolder.hemoCubeTestData?.deviceRatio + testDetails?.predictedDenovixRatio = DataHolder.hemoCubeTestData?.predictedDenovixRatio testDetails?.calculatedRatio = DataHolder.hemoCubeTestData?.calculatedRatio testDetails?.coefficients = DataHolder.hemoCubeTestData?.coefficients testDetails?.classificationResult = DataHolder.hemoCubeTestData?.classificationResult!! + testDetails?.prdClassification = DataHolder.hemoCubeTestData?.prdClassification.toString() + testDetails?.batteryLevel = DataHolder.hemoCubeTestData?.batteryLevel.toString() + testDetails?.batteryCapacity = DataHolder.hemoCubeTestData?.batteryCapacity.toString() + testDetails?.batteryMaxCapacity = DataHolder.hemoCubeTestData?.batteryMaxCapacity.toString() + testDetails?.batteryTemperature = DataHolder.hemoCubeTestData?.batteryTemperature.toString() + testDetails?.batteryVoltage = DataHolder.hemoCubeTestData?.batteryVoltage.toString() } private fun addResultTestToDb() { @@ -189,7 +210,56 @@ class HemoCubeViewModel @Inject constructor( hemoCubeDao.updateFieldById(id = userId, true) } + fun addUser(userData: HemoCubeTestData) = viewModelScope.launch { + hemoCubeDao.insertAll(userData) + } + fun deleteById(userId: String) = viewModelScope.launch { hemoCubeDao.deleteById(id = userId) } + + fun getBatteryLevel(): Float? { + val batteryPct: Float? = batteryStatus?.let { intent -> + val level: Int = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) + val scale: Int = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1) + level * 100 / scale.toFloat() + } + + return batteryPct + } + + fun getBatteryTemperature(): Float? { + val batteryTemp: Float? = batteryStatus?.let { intent -> + val temperature = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0 + temperature.toFloat() / 10 + } + + return batteryTemp + } + + fun getBatteryVoltage(context: Context): Float { + val batteryIntent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) + val voltage = batteryIntent?.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0) ?: 0 + + // milli-volts to volts + return voltage.toFloat() / 1000 + } + + fun getBatteryCapacity(context: Context): Int { + val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager + val currentCapacity = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER) + + return currentCapacity + } + + fun getBatteryMaxCapacity(context: Context): Float { + val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager + val designCapacity = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) + val currentCapacity = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER) + + // Calculate the estimated maximum battery capacity in mAh + val maxCapacity = currentCapacity.toFloat() / designCapacity.toFloat() * 100 + + return maxCapacity + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 9aa32ec..87f4636 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -22,7 +22,7 @@ android:id="@+id/internetNotAvailableCL" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="gone" + android:visibility="visible" android:padding="24dp"> + + + + + + app:layout_constraintTop_toBottomOf="@+id/til_blood_group" /> + + @@ -74,7 +106,7 @@ android:id="@+id/internetAvailableCL" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="visible" + android:visibility="gone" android:padding="24dp"> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5f1e0da..0d6d018 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -183,10 +183,10 @@ User List User ID Aadhar ID - Internet not available, please enter the user ID manually - User ID should be 18 digits - Upload DB Registration - Do you want to upload the local DB Registration to the cloud? + Internet not available, please enter the user ID and blood group manually + User ID should be 18 digits and please select the blood group + Upload DB Tests + Do you want to upload the local DB tests to the cloud? Upload Cancel Upload done successfully diff --git a/app/src/test/java/com/example/hpostesting/HemoCubeViewModelTest.kt b/app/src/test/java/com/example/hpostesting/HemoCubeViewModelTest.kt new file mode 100644 index 0000000..012036e --- /dev/null +++ b/app/src/test/java/com/example/hpostesting/HemoCubeViewModelTest.kt @@ -0,0 +1,127 @@ +package com.example.hpostesting + +import android.content.Context +import android.content.SharedPreferences +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.LiveData +import com.example.hpostesting.data.dao.HemoCubeDao +import com.example.hpostesting.data.model.Response +import com.example.hpostesting.data.model.patient.HemoCubeTestData +import com.example.hpostesting.data.model.patient.toHemoCubeTestData +import com.example.hpostesting.data.repository.DatabaseRepository +import com.example.hpostesting.data.repository.Repository +import com.example.hpostesting.presentation.hemocube.HemoCubeFragment +import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel +import com.example.hpostesting.presentation.hemocube.HemocubeActivity +import com.example.hpostesting.util.TestCoroutineRule +import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@ExperimentalCoroutinesApi +class HemoCubeViewModelTest { + + // Add rule for testing LiveData + @get:Rule + val rule = InstantTaskExecutorRule() + + // Add rule for testing coroutines + @get:Rule + val coroutineRule = TestCoroutineRule() + + // Mock dependencies + @Mock + lateinit var hemoCubeDao: HemoCubeDao + + @Mock + lateinit var repository: Repository + + // Mock context + @Mock + lateinit var context: Context + + // Mock LiveData for testing + @Mock + lateinit var networkStatusLiveData: LiveData + + // Initialize the ViewModel + lateinit var viewModel: HemoCubeViewModel + + private lateinit var hemoCubeFragment: HemoCubeFragment + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) +// viewModel = HemoCubeViewModel(hemoCubeDao, repository, context) + hemoCubeFragment = HemoCubeFragment() + } + + @Test + fun `uploadHemoCubeResultToDatabase with online status should call addResultTestToDb`() = + coroutineRule.runBlockingTest { + // Mock data and setup + val isOnline = true + val testStatus = true + val kitSerial = "12345" + + // Mock the necessary methods + `when`(repository.addTestToDatabase(viewModel.testDetails!!)) + .thenReturn(Response.Success("Success")) + + // Call the function to be tested + viewModel.uploadHemoCubeResultToDatabase(isOnline, testStatus, kitSerial) + + // Verify that addResultTestToDb is called + advanceUntilIdle() + assertEquals("Local", viewModel.fireBaseUpload.value) + } + + // Similar tests can be written for other methods in HemoCubeViewModel + + @Mock + private lateinit var mockSharedPreferences: SharedPreferences + + @Mock + private lateinit var mockActivity: HemocubeActivity // Replace with your actual Activity class + + @Mock + private lateinit var mockBinding: FragmentHemoCubeReferenceBinding // Replace with your actual Binding class + + @Test + fun `findResult with valid input`() { + // Arrange + val validString = "valid string" + val fullReadOutput = "full read output" + `when`(mockSharedPreferences.getString(anyString(), anyString())).thenReturn("dummy_value") + `when`(mockActivity.runOnUiThread(any())).thenAnswer { + val runnable = it.getArgument(0, Runnable::class.java) + runnable.run() + } + + // Act + val result = hemoCubeFragment.findResult(calculatedRatio = 0.06) + + // Assert + // Add appropriate assertions based on the behavior you expect +// verify(mockSharedPreferences).edit() +// verify(mockBinding).btnSubmit.visibility = View.VISIBLE +// verify(mockBinding).btnSubmit.isEnabled = true + // Add more verifications as needed + + assertEquals("Normal", result) + } + + + +} \ No newline at end of file diff --git a/app/src/test/java/com/example/hpostesting/TestRightViewModelTest.kt b/app/src/test/java/com/example/hpostesting/TestRightViewModelTest.kt index 72ec827..2be9212 100644 --- a/app/src/test/java/com/example/hpostesting/TestRightViewModelTest.kt +++ b/app/src/test/java/com/example/hpostesting/TestRightViewModelTest.kt @@ -11,91 +11,91 @@ import java.math.RoundingMode import java.text.DecimalFormat class TestRightViewModelTest { - private val viewModel = TestRightViewModel() - private val data = InputData() - - @Test - fun test_mapDeviceConstants() { - viewModel.mapDeviceConstants(data.inputRead) - assertEquals("0", DataHolder.deviceConstant!!.a) - assertEquals("1.69989422e-06", DataHolder.deviceConstant!!.b) - assertEquals("1.60642711e-01", DataHolder.deviceConstant!!.c) - assertEquals("3.85754470e+02", DataHolder.deviceConstant!!.d) - } - - @Test - fun test_mapPixelNumberToWavelength() { - viewModel.mapDeviceConstants(data.inputRead) - viewModel.mapPixelNumberToWavelength() - - val outputList = TestDataGenerator().getOutputMapPixelNumberToWavelength() - - assertEquals(Constants.TEST_RIGHT_TOTAL_PIXEL, DataHolder.wavelengthToPixelArray.size) - assertEquals(Constants.TEST_RIGHT_TOTAL_PIXEL, outputList.size) - - val df = DecimalFormat("#.###") - df.roundingMode = RoundingMode.FLOOR - - for (i in 0 until Constants.TEST_RIGHT_TOTAL_PIXEL){ - assertEquals(df.format(outputList[i]), df.format(DataHolder.wavelengthToPixelArray[i])) - } - - } - +// private val viewModel = TestRightViewModel() +// private val data = InputData() +// // @Test -// fun test_mapIntensityValues() { -// val inputReference = TestDataGenerator().getInputReferenceMapIntensityValues() -// val inputSample = TestDataGenerator().getInputSampleMapIntensityValues() -// -// viewModel.mapIntensityValues(inputReference, true) -// viewModel.mapIntensityValues(inputSample, false) -// -// val outputReference = TestDataGenerator().getOutputReferenceMapIntensityValues() -// val outputSample = TestDataGenerator().getOutputSampleMapIntensityValues() -// -// assertEquals(outputReference, DataHolder.intensityReferenceArray) -// assertEquals(outputSample, viewModel.intensitySampleArray) +// fun test_mapDeviceConstants() { +// viewModel.mapDeviceConstants(data.inputRead) +// assertEquals("0", DataHolder.deviceConstant!!.a) +// assertEquals("1.69989422e-06", DataHolder.deviceConstant!!.b) +// assertEquals("1.60642711e-01", DataHolder.deviceConstant!!.c) +// assertEquals("3.85754470e+02", DataHolder.deviceConstant!!.d) +// } +// +// @Test +// fun test_mapPixelNumberToWavelength() { +// viewModel.mapDeviceConstants(data.inputRead) +// viewModel.mapPixelNumberToWavelength() +// +// val outputList = TestDataGenerator().getOutputMapPixelNumberToWavelength() +// +// assertEquals(Constants.TEST_RIGHT_TOTAL_PIXEL, DataHolder.wavelengthToPixelArray.size) +// assertEquals(Constants.TEST_RIGHT_TOTAL_PIXEL, outputList.size) +// +// val df = DecimalFormat("#.###") +// df.roundingMode = RoundingMode.FLOOR +// +// for (i in 0 until Constants.TEST_RIGHT_TOTAL_PIXEL){ +// assertEquals(df.format(outputList[i]), df.format(DataHolder.wavelengthToPixelArray[i])) +// } +// +// } +// +//// @Test +//// fun test_mapIntensityValues() { +//// val inputReference = TestDataGenerator().getInputReferenceMapIntensityValues() +//// val inputSample = TestDataGenerator().getInputSampleMapIntensityValues() +//// +//// viewModel.mapIntensityValues(inputReference, true) +//// viewModel.mapIntensityValues(inputSample, false) +//// +//// val outputReference = TestDataGenerator().getOutputReferenceMapIntensityValues() +//// val outputSample = TestDataGenerator().getOutputSampleMapIntensityValues() +//// +//// assertEquals(outputReference, DataHolder.intensityReferenceArray) +//// assertEquals(outputSample, viewModel.intensitySampleArray) +//// } +// +// @Test +// fun test_mapWavelengthToAbsorbance() { +// +// viewModel.mapDeviceConstants(data.inputRead) +// viewModel.mapPixelNumberToWavelength() +// viewModel.mapIntensityValues(TestDataGenerator().getInputReferenceMapIntensityValues(), true) +// viewModel.mapIntensityValues(TestDataGenerator().getInputSampleMapIntensityValues(), false) +// viewModel.patientDetails = PatientData("Surya", 2, "Male", TestRightResultType.UNDEFINED) +// viewModel.mapWavelengthToAbsorbance() +// +// val wavelengthList = TestDataGenerator().getOutputMapPixelNumberToWavelength() +// val absorbanceList = TestDataGenerator().getOutputMapWavelengthToAbsorbance() +// +// val df = DecimalFormat("#.###") +// df.roundingMode = RoundingMode.FLOOR +// +// assertEquals(Constants.TEST_RIGHT_TOTAL_PIXEL, viewModel.wavelengthToAbsorbance.size) +// for (i in 0 until Constants.TEST_RIGHT_TOTAL_PIXEL){ +// assertEquals(df.format(wavelengthList[i]), df.format(viewModel.wavelengthToAbsorbance[i][0])) +// assertEquals(df.format(absorbanceList[i]), df.format(viewModel.wavelengthToAbsorbance[i][1])) +// } +// } +// +// +// @Test +// fun testRightViewModel_calculateDataForCSV() { +// +// viewModel.mapDeviceConstants(data.inputRead) +// viewModel.mapPixelNumberToWavelength() +// viewModel.mapIntensityValues(data.printForReference, true) +// viewModel.mapIntensityValues(data.printForSample, false) +//// viewModel.patientDetails = PatientData("Surya", 2, "Male", null) +//// viewModel.calculateResults() +// viewModel.mapWavelengthToAbsorbance() +// +// for (each in viewModel.wavelengthToAbsorbance){ +// println(each[0].toString() + " -> " + each[1]) +// } // } - - @Test - fun test_mapWavelengthToAbsorbance() { - - viewModel.mapDeviceConstants(data.inputRead) - viewModel.mapPixelNumberToWavelength() - viewModel.mapIntensityValues(TestDataGenerator().getInputReferenceMapIntensityValues(), true) - viewModel.mapIntensityValues(TestDataGenerator().getInputSampleMapIntensityValues(), false) - viewModel.patientDetails = PatientData("Surya", 2, "Male", TestRightResultType.UNDEFINED) - viewModel.mapWavelengthToAbsorbance() - - val wavelengthList = TestDataGenerator().getOutputMapPixelNumberToWavelength() - val absorbanceList = TestDataGenerator().getOutputMapWavelengthToAbsorbance() - - val df = DecimalFormat("#.###") - df.roundingMode = RoundingMode.FLOOR - - assertEquals(Constants.TEST_RIGHT_TOTAL_PIXEL, viewModel.wavelengthToAbsorbance.size) - for (i in 0 until Constants.TEST_RIGHT_TOTAL_PIXEL){ - assertEquals(df.format(wavelengthList[i]), df.format(viewModel.wavelengthToAbsorbance[i][0])) - assertEquals(df.format(absorbanceList[i]), df.format(viewModel.wavelengthToAbsorbance[i][1])) - } - } - - - @Test - fun testRightViewModel_calculateDataForCSV() { - - viewModel.mapDeviceConstants(data.inputRead) - viewModel.mapPixelNumberToWavelength() - viewModel.mapIntensityValues(data.printForReference, true) - viewModel.mapIntensityValues(data.printForSample, false) -// viewModel.patientDetails = PatientData("Surya", 2, "Male", null) -// viewModel.calculateResults() - viewModel.mapWavelengthToAbsorbance() - - for (each in viewModel.wavelengthToAbsorbance){ - println(each[0].toString() + " -> " + each[1]) - } - } } \ No newline at end of file diff --git a/app/src/test/java/com/example/hpostesting/util/TestCoroutineRule.kt b/app/src/test/java/com/example/hpostesting/util/TestCoroutineRule.kt new file mode 100644 index 0000000..cb20d2c --- /dev/null +++ b/app/src/test/java/com/example/hpostesting/util/TestCoroutineRule.kt @@ -0,0 +1,28 @@ +package com.example.hpostesting.util + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.rules.TestWatcher +import org.junit.runner.Description + +@ExperimentalCoroutinesApi +class TestCoroutineRule : TestWatcher(), TestCoroutineScope by TestCoroutineScope() { + + private val testCoroutineDispatcher = TestCoroutineDispatcher() + + override fun starting(description: Description?) { + super.starting(description) + Dispatchers.setMain(testCoroutineDispatcher) + } + + override fun finished(description: Description?) { + super.finished(description) + Dispatchers.resetMain() + cleanupTestCoroutines() + } +} \ No newline at end of file