Compare commits

...

16 Commits

Author SHA1 Message Date
Pritimay Sarkar
3609d728c1 release 2.1.110 2024-02-10 12:18:52 +05:30
Pritimay Sarkar
7302ad669f release 2.1.108 2024-02-10 11:10:06 +05:30
Pritimay Sarkar
ef98b09eaf release 2.1.107 2024-02-10 10:46:54 +05:30
Pritimay Sarkar
f631a273d7 TLS impleamentation on NATS server 2024-02-09 13:50:33 +05:30
Pritimay Sarkar
de74da3135 add package name dynamically in provider 2024-02-09 13:49:16 +05:30
Mariya
37c8dde5bd Added code in manifest file for launcher 2024-02-08 15:44:27 +05:30
Mariya
5f460f6aa3 Added code for firefox and Files redirection 2024-02-08 15:30:51 +05:30
Pritimay Sarkar
ad625992a7 fix pipeline 2024-02-08 14:41:27 +05:30
Pritimay Sarkar
db518007b3 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-02-08 12:33:49 +05:30
Pritimay Sarkar
4d09bad516 log addititonal data in diagnostics and auto dac 2024-02-08 12:33:36 +05:30
Mariya
aad33e3907 Merge remote-tracking branch 'origin/dev' into dev 2024-02-08 11:59:06 +05:30
Mariya
52279c4419 code removed from manifest for molbio 2024-02-08 11:58:45 +05:30
Pritimay Sarkar
190a72e407 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-02-07 17:19:33 +05:30
Pritimay Sarkar
4aa6fb020b display device ratio and slope ratio 2024-02-07 16:25:34 +05:30
Mariya
044a545979 code added in manifest file 2024-02-07 13:01:09 +05:30
Mariya
1f9f8060cb manifest file changes and drawable file added for release apk 2024-02-06 16:48:19 +05:30
16 changed files with 151 additions and 42 deletions

View File

@@ -19,8 +19,8 @@ android {
applicationId "in.sminnovations.hpostesting.quality"
minSdk 21
targetSdk 34
versionCode 101
versionName "2.1.101"
versionCode 106
versionName "2.1.106"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

View File

@@ -95,6 +95,7 @@
android:exported="false"
android:label="@string/title_activity_dashboard"
android:theme="@style/Theme.HPOS.NoActionBar"
android:screenOrientation="portrait"
tools:ignore="AppLinkUrlError,MissingClass">
<intent-filter>
@@ -119,8 +120,12 @@
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
</activity>
<activity
@@ -160,7 +165,7 @@
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.hpostesting.fileprovider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@@ -5,5 +5,8 @@ data class DiagnosticsData (
var appVersion: String? = "",
var deviceType: String = "HEMOCUBE",
var deviceData: String = "",
var devicePassword: String = "",
var deviceNatsToken: String = "",
var accessToken: String = "",
var runTime: String = ""
)

View File

@@ -8,6 +8,7 @@ import io.nats.client.Message
import io.nats.client.NKey
import io.nats.client.Nats
import io.nats.client.Options
import io.nats.client.support.SSLUtils
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.security.GeneralSecurityException
@@ -30,7 +31,8 @@ class NatsManager(datacollector: DashboardActivity) {
val theNKey = NKey.fromSeed(seedBytes) // really should load from somewhere
val options = Options.Builder()
.server("nats://192.168.10.117:4222")
.server("nats://nanodgx.in:4222")
.sslContext(SSLUtils.createOpenTLSContext())
.authHandler(object : AuthHandler {
override fun getID(): CharArray? {
return try {

View File

@@ -12,6 +12,7 @@ import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
@@ -30,6 +31,9 @@ class AssuranceControlsFragment: Fragment() {
binding = FragmentAssuranceControlsBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
DataHolder.hemoCubeTestData!!.solution = ""
DataHolder.hemoCubeTestData!!.volume = ""
return binding.root
}
@@ -42,7 +46,7 @@ class AssuranceControlsFragment: Fragment() {
// binding.btnSubmit.visibility = View.GONE
val solutionSpinner: Spinner = binding.spinnerSolutions
val solutionOptions = arrayOf("Select solution", "Tartrazine", "AR")
val solutionOptions = arrayOf("Select solution", "Tartrazine", "Acid Red")
val solutionAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, solutionOptions)
solutionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
solutionSpinner.adapter = solutionAdapter
@@ -121,6 +125,13 @@ class AssuranceControlsFragment: Fragment() {
volumeSpinner.setSelection(volumePosition)
binding.btnSubmit.setOnClickListener {
val selectedSolution = DataHolder.hemoCubeTestData!!.solution
val selectedVolume = DataHolder.hemoCubeTestData!!.volume
if (selectedSolution == "Select solution" || selectedVolume == "Select volume") {
Toast.makeText(requireContext(), "Please select both solution and volume", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
DataHolder.hemoCubeTestData!!.quickCapture = true
val currentUnixTime = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Instant.now().epochSecond

View File

@@ -105,8 +105,6 @@ class AutoDacFragment: Fragment() {
HemoCubeCommands.AUTO_DAC_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
}
override fun onUsbError(e: Exception?) {
autoDacViewModel.progressBar.postValue(false)
@@ -148,6 +146,9 @@ class AutoDacFragment: Fragment() {
autoDacViewModel.addAutoDacDataToDb(
DiagnosticsData(
deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(),
devicePassword = sharedPreferences.getString(Constants.DEVICE_PASSWORD_API, "").toString(),
deviceNatsToken = sharedPreferences.getString(Constants.NATS_TOKEN, "").toString(),
accessToken = sharedPreferences.getString(Constants.ACCESS_TOKEN, "").toString(),
deviceData = resultData,
runTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
@@ -169,7 +170,6 @@ class AutoDacFragment: Fragment() {
}
}
fun parseData(inputData: List<String>): List<Pair<String, String>> {
val pattern = Regex("([A-Z]+)\\s(\\d+)")
val parsedData = mutableListOf<Pair<String, String>>()

View File

@@ -156,9 +156,10 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
val file = File(getExternalFilesDir("Updates"), "update.apk")
file.setReadable(true, false) // Ensure the file is readable
val pInfo = baseContext.packageManager.getPackageInfo(baseContext.packageName, 0)
val uri: Uri = FileProvider.getUriForFile(
this,
"com.example.hpostesting.fileprovider",
"${pInfo}.fileprovider",
file
)

View File

@@ -1,6 +1,7 @@
package com.example.hpostesting.presentation.dashboard
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
@@ -14,12 +15,13 @@ import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.presentation.autodac.AutoDacActivity
import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity
import com.example.hpostesting.presentation.calibration.CalibrationActivity
import com.example.hpostesting.presentation.deviceprovision.DeviceProvisionActivity
import com.example.hpostesting.presentation.deviceinfo.DeviceActivity
import com.example.hpostesting.presentation.deviceprovision.DeviceProvisionActivity
import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import `in`.sminnovations.hpostesting.databinding.FragmentGalleryBinding
class GalleryFragment : Fragment() {
private var _binding: FragmentGalleryBinding? = null
@@ -82,6 +84,19 @@ class GalleryFragment : Fragment() {
startActivity(Intent(requireContext(), DeviceActivity::class.java))
}
binding.btnFirefox.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.component = ComponentName("org.mozilla.firefox", "org.mozilla.gecko.BrowserApp")
startActivity(intent)
}
binding.btnFiles.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "file/*"
startActivity(intent)
}
userid = sharedPreferences.getString(Constants.USER_ID, "").toString()
binding.tvSubtitle4.text = "Login ID : ${userid}"

View File

@@ -203,6 +203,7 @@ class HomeFragment : Fragment() {
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
hemoCubeViewModel.uploadLogs()
hemoCubeViewModel.startPeriodicCheckUpdate()
}
}
} else {

View File

@@ -114,7 +114,6 @@ class DeviceProvisionFragment : Fragment() {
}
}
private fun getDeviceId() {
(activity as DeviceProvisionActivity).mService.sendAndListenToHemoCube(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
@@ -146,14 +145,19 @@ class DeviceProvisionFragment : Fragment() {
}
}
fun extractV2HardwareId(input: String): String? {
val pattern = Regex("SNS\\s*(.*?)\\s*SNE")
val matchResult: MatchResult? = pattern.find(input)
return matchResult?.groups?.get(1)?.value
}
private fun handleUsbData() {
when {
resultData.contains("SNE") -> {
val pattern = Regex("HPP1-\\d{4}")
val matchResult = pattern.find(resultData)
val hardwareId = matchResult?.value
val hardwareId = extractV2HardwareId(resultData)
if (hardwareId.toString().length == 9) {
if (!hardwareId.isNullOrBlank()) {
with(sharedPreferences.edit()) {
putString(Constants.DEVICE_ID, hardwareId)
apply()

View File

@@ -148,6 +148,9 @@ class DiagnosticsFragment : Fragment() {
if (resultData.contains("END") || fullReadOutput.contains("END")) {
diagnosticsViewModel.addDiagnosticsDataToDb(DiagnosticsData(
deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(),
devicePassword = sharedPreferences.getString(Constants.DEVICE_PASSWORD_API, "").toString(),
deviceNatsToken = sharedPreferences.getString(Constants.NATS_TOKEN, "").toString(),
accessToken = sharedPreferences.getString(Constants.ACCESS_TOKEN, "").toString(),
deviceData = resultData,
runTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()

View File

@@ -643,10 +643,10 @@ class HemoCubeFragment : Fragment() {
if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) {
assignDefaultDevice(resultData)
testState.allErrorMessages += "Calibration configuration for this device id is not found\n"
// testState.allErrorMessages += "Calibration configuration for this device id is not found\n"
}
if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) {
testState.allErrorMessages += "ADC thresholds for this device id are not found\n"
// testState.allErrorMessages += "ADC thresholds for this device id are not found\n"
}
}
@@ -743,9 +743,9 @@ class HemoCubeFragment : Fragment() {
)?.get(0)!!
) {
// validationError = true
testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)"
// testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)"
activity?.runOnUiThread {
binding.errorMessage.text = getString(R.string.error_improper_buffer_low)
// binding.errorMessage.text = getString(R.string.error_improper_buffer_low)
// binding.errorMessage.visibility = View.VISIBLE
}
}
@@ -763,11 +763,11 @@ class HemoCubeFragment : Fragment() {
)?.get(1)!!
) {
// validationError = true
testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n"
// testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n"
activity?.runOnUiThread {
binding.errorMessage.text =
// binding.errorMessage.text =
getString(R.string.error_improper_buffer_high)
binding.errorMessage.visibility = View.VISIBLE
// binding.errorMessage.visibility = View.VISIBLE
}
}
@@ -811,9 +811,9 @@ class HemoCubeFragment : Fragment() {
if (fittedAbs1 <= fittedAbs2) {
// validationError = true
testState.allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n"
// testState.allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n"
activity?.runOnUiThread {
binding.errorMessage.text = getString(R.string.error_invalid_test)
// binding.errorMessage.text = getString(R.string.error_invalid_test)
// binding.errorMessage.visibility = View.VISIBLE
}
}
@@ -837,9 +837,9 @@ class HemoCubeFragment : Fragment() {
if (fittedAbs3 < 0.1) {
// validationError = true
testState.allErrorMessages += "Error: Low Hb. Repeat test" + "\n"
// testState.allErrorMessages += "Error: Low Hb. Repeat test" + "\n"
activity?.runOnUiThread {
binding.errorMessage.text = "Error: Low Hb. Repeat test"
// binding.errorMessage.text = "Error: Low Hb. Repeat test"
// binding.errorMessage.visibility = View.VISIBLE
}
}
@@ -874,8 +874,8 @@ class HemoCubeFragment : Fragment() {
this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio)
this.deviceRatioClass = deviceRatioClassification(deviceRatio)
this.slopeRatioClass = slopeClass
this.classificationResult = findResultWithAdditionalMethods(deviceRatio, deviceRatioClass, slopeRatio)
hemoCubeViewModel.messages.postValue("${this.classificationResult} ")
this.classificationResult = deviceRatioClass
hemoCubeViewModel.messages.postValue("${this.classificationResult} \n Device Ratio: ${"%.3f".format(this.deviceRatio)}")
if (DataHolder.hemoCubeTestData?.testType == "HB")
hemoCubeViewModel.messages.postValue("Hb: $calculatedHb4")
this.errorMessages = testState.allErrorMessages
@@ -914,7 +914,7 @@ class HemoCubeFragment : Fragment() {
// hemoCubeViewModel.messages.postValue("post classification checks")
if (deviceRatio != null) {
if (slopeRatio != null) {
if (deviceRatioClass == "Normal" && slopeRatio > 60.0)
if (deviceRatioClass == "Normal" && slopeRatio > 45.0)
return "Negative Borderline, Repeat Test"
}
}
@@ -928,17 +928,17 @@ class HemoCubeFragment : Fragment() {
fun deviceRatioClassification(ratio: Double?): String {
try {
if (ratio != null) {
if (ratio in 0.001..0.23) {
if (ratio in 0.016..0.22) {
// setSubtitleTextColor(R.color.green_2)
return "Normal"
}
if (ratio in 0.23..0.24)
return "Negative Borderline, Repeat Test"
if (ratio in 0.24..0.29)
if (ratio in 0.22..0.24)
return "Negative Borderline"
if (ratio in 0.24..0.32)
return "Sickle Cell Trait"
if (ratio in 0.29..0.32)
if (ratio in 0.32..0.37)
return "Positive for Sickle Cell. HPLC for Confirmation"
if (ratio in 0.32..Double.POSITIVE_INFINITY)
if (ratio in 0.37..0.56)
return "Sickle Cell Disease"
} else {
return "Invalid"

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="@color/primary"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/primary" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector>

View File

@@ -108,4 +108,33 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_calibration" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_firefox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="@string/Firefox"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_deviceProvision" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_files"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="24dp"
android:clickable="false"
android:text="@string/Files"
android:textColor="@color/white"
app:cornerRadius="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_firefox" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -115,6 +115,8 @@
<string name="assurance_controls">Quality Assurance</string>
<string name="calibration">Calibration</string>
<string name="deviceProvision">Device Provision</string>
<string name="Firefox">Firefox</string>
<string name="Files">Files</string>
<string name="deviceinfo">Device Information</string>
<string name="place_buffer">Start</string>
<string name="Start_Sample">Start Sample</string>

View File

@@ -327,7 +327,7 @@ class HemoCubeFragmentTest {
fun testDeviceRatioClassificationNegativeBorderline() {
val ratio = 0.235
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Negative Borderline, Repeat Test", result)
assertEquals("Negative Borderline", result)
}
@Test
@@ -339,7 +339,7 @@ class HemoCubeFragmentTest {
@Test
fun testDeviceRatioClassificationPositiveForSickleCell() {
val ratio = 0.31
val ratio = 0.37
val result = hemoCubeFragment.deviceRatioClassification(ratio)
assertEquals("Positive for Sickle Cell. HPLC for Confirmation", result)
}
@@ -366,9 +366,37 @@ class HemoCubeFragmentTest {
}
@Test
fun findResultWithAdditionalMethods_ValidInput_ReturnsDeviceRatioClassToString() {
fun findResultWithAdditionalMethods_NormalDeviceRatio_ReturnsNormalBelowSlopeRatioThreshold() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Normal", 70.0)).thenReturn("Negative Borderline, Repeat Test")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Normal", 30.0)
assertEquals("Normal", result)
}
@Test
fun findResultWithAdditionalMethods_NBL_ReturnsNBL() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)
assertEquals("Abnormal", result)
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Negative Borderline, Repeat Test", 70.0)
assertEquals("Negative Borderline, Repeat Test", result)
}
@Test
fun findResultWithAdditionalMethods_SCT_ReturnsSCT() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Sickle Cell Trait", 70.0)
assertEquals("Sickle Cell Trait", result)
}
@Test
fun findResultWithAdditionalMethods_PBL_ReturnsPBL() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Positive for Sickle Cell. HPLC for Confirmation", 70.0)
assertEquals("Positive for Sickle Cell. HPLC for Confirmation", result)
}
@Test
fun findResultWithAdditionalMethods_SCD_ReturnsSCD() {
// `when`(hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Abnormal", 70.0)).thenReturn("Invalid")
val result = hemoCubeFragment.findResultWithAdditionalMethods(0.5, "Sickle Cell Disease", 70.0)
assertEquals("Sickle Cell Disease", result)
}
}