From 88cdc01978d207dc9dbb9921ff8b33406317ff97 Mon Sep 17 00:00:00 2001 From: chandrashekhar reddy Date: Mon, 24 Feb 2025 19:02:08 +0530 Subject: [PATCH] app version 132 - Added changes according to single test and also added upload csv to firebase storage --- .../hpostesting/data/constant/Constants.kt | 22 +++ .../datasource/LocalFileDataSourceImpl.kt | 23 +-- .../presentation/KitScanActivity.kt | 47 ++++-- .../main_base/SettingsFragment.kt | 134 +++++++++++++++++- .../trueheme_test/TrueHemeTestFragment.kt | 63 ++++++++ .../trueheme_test/TrueHemeTestViewModel.kt | 41 ++++++ .../main/res/layout/fragment_slideshow.xml | 14 +- 7 files changed, 317 insertions(+), 27 deletions(-) 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 0df960f..1d48de3 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 @@ -47,6 +47,7 @@ object Constants { const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE = 34 const val MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM = 9 + const val MAXIMUM_TEST_ALLOWED_SINGLE_TEST = 0 const val RANGE_IN_RESULT_CALCULATIONS = 10 @@ -1586,6 +1587,7 @@ object Constants { //Trueheme for 10mm //Before changing below values review before //classification borderline metric + const val TEST_FOR_10MM = "SMI/SC-2-D10/" const val positiveBoderLineMetricCheck10mmMin = 1.3 const val positiveBoderLineMetricCheck10mmMax = 1.66 const val negativeBoderLineMetricCheck10mmMin = 2.0 @@ -1603,6 +1605,7 @@ object Constants { const val sickleCellDiseaseMax10mm = 0.7 //Trueheme for 2mm //classification borderline metric + const val TEST_FOR_2MM = "SMI/SC/" const val positiveBoderLineMetricCheck2mmMin = 0.8 const val positiveBoderLineMetricCheck2mmMax = 1.1 const val negativeBoderLineMetricCheck2mmMin = 1.5 @@ -1619,6 +1622,25 @@ object Constants { const val sickleCellDiseaseMin2mm = 0.45 const val sickleCellDiseaseMax2mm = 0.7 + //Trueheme for 2mm Single Test + //classification borderline metric + const val singleTest = "SMI/SC-ST/" + const val positiveBoderLineMetricCheck2mmStMin = 1.3 + const val positiveBoderLineMetricCheck2mmStMax = 1.66 + const val negativeBoderLineMetricCheck2mmStMin = 2.0 + const val negativeBoderLineMetricCheck2mmStMax = 2.4 + //classification device ratio + const val normalMinSt2mm = 0.07 + const val normalMaxSt2mm = 0.23 + const val negativeBorderlineMinSt2mm = 0.23 + const val negativeBorderlineMaxSt2mm = 0.27 + const val sickleCellTraitMinSt2mm = 0.27 + const val sickleCellTraitMaxSt2mm = 0.31 + const val positiveForSickleCellMinSt2mm = 0.31 + const val positiveForSickleCellMaxSt2mm = 0.39 + const val sickleCellDiseaseMinSt2mm = 0.39 + const val sickleCellDiseaseMaxSt2mm = 0.7 + const val min2mmLed1 = 0.34 const val max2mmLed1 = 1.48 const val min2mmLed2 = 0.04 diff --git a/app/src/main/java/com/example/hpostesting/data/datasource/LocalFileDataSourceImpl.kt b/app/src/main/java/com/example/hpostesting/data/datasource/LocalFileDataSourceImpl.kt index 63154a3..4fe7af7 100644 --- a/app/src/main/java/com/example/hpostesting/data/datasource/LocalFileDataSourceImpl.kt +++ b/app/src/main/java/com/example/hpostesting/data/datasource/LocalFileDataSourceImpl.kt @@ -38,15 +38,16 @@ class LocalFileDataSourceImpl @Inject constructor() : LocalFileDataSource { } override fun exportDataToCSV( - fileName: String, dataList: List, + filePath: String, dataList: List, ): Boolean { try { - val formattedFileName = fileName.replace( - Regex("[^a-zA-Z0-9.-]"), - "_" - ) // Replace special characters with underscores - val filePath = File(getExternalStorageDirectory(), formattedFileName) - val writer = FileWriter(filePath) + // Create the file directly from the provided path + val file = File(filePath) + + // Ensure parent directory exists + file.parentFile?.mkdirs() + + val writer = FileWriter(file) val csvWriter = CSVWriter(writer) // Write CSV header val header = arrayOf( @@ -103,8 +104,8 @@ class LocalFileDataSourceImpl @Inject constructor() : LocalFileDataSource { csvWriter.writeNext(header) // Filter and write data rows where testStatus is true - val filteredDataList = dataList.filter { it.testStatus == true } - for (data in filteredDataList) { + // val filteredDataList = dataList.filter { it.testStatus == true } + for (data in dataList) { val row = arrayOf( data._id, data.name, @@ -226,8 +227,8 @@ class LocalFileDataSourceImpl @Inject constructor() : LocalFileDataSource { csvWriter.writeNext(header) // Filter and write data rows where testStatus is true - val filteredDataList = dataList.filter { it.testStatus == true } - for (data in filteredDataList) { + //val filteredDataList = dataList.filter { it.testStatus == true } + for (data in dataList) { val row = arrayOf( data._id, data.name, diff --git a/app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt index 8f62e10..34d26f3 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt @@ -24,6 +24,9 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.example.hpostesting.data.constant.DataHolder import com.example.hpostesting.data.constant.Constants +import com.example.hpostesting.data.constant.Constants.TEST_FOR_10MM +import com.example.hpostesting.data.constant.Constants.TEST_FOR_2MM +import com.example.hpostesting.data.constant.Constants.singleTest import com.example.hpostesting.data.constant.LanguageManager import com.example.hpostesting.data.model.test.TestType import com.example.hpostesting.presentation.main_base.DashboardActivity @@ -52,7 +55,7 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate { private var fromWhere = "Home" private val TAG = "KitScanActivity" private lateinit var binding: ActivityKitScanBinding - + private var maxTest = 9 private lateinit var sharedPreference: SharedPreferences var sdkHandler: SDKHandler? = null @@ -71,20 +74,27 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate { } private fun processScannedData(contents: String) {//edited auto selection of cuvette size - if(contents.contains("SMI/SC/")){ + if(contents.contains(TEST_FOR_2MM)){ with(sharedPreference.edit()) { putString(Constants.CUVETTE_SIZE, "2mm") apply() } Toast.makeText(this, "Selected cuvette size: 2mm", Toast.LENGTH_SHORT).show() binding.nameEditText.setText(contents) - }else if(contents.contains("SMI/SC-2-D10/")){ + }else if(contents.contains(TEST_FOR_10MM)){ with(sharedPreference.edit()) { putString(Constants.CUVETTE_SIZE, "10mm") apply() } Toast.makeText(this, "Selected cuvette size: 10mm", Toast.LENGTH_SHORT).show() binding.nameEditText.setText(contents) + }else if(contents.contains(singleTest)){ + with(sharedPreference.edit()) { + putString(Constants.CUVETTE_SIZE, "2mmSt") + apply() + } + Toast.makeText(this, "Selected cuvette size: 2mm for single test", Toast.LENGTH_SHORT).show() + binding.nameEditText.setText(contents) }else{ Toast.makeText(this, R.string.invalid_kit, Toast.LENGTH_LONG).show() } @@ -133,10 +143,12 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate { setContentView(binding.root) binding.toolbar.title = "Kit Serial Number" fromWhere = intent.getStringExtra("fromWhere").toString() - val maxTest = if(sharedPreference.getString(Constants.CUVETTE_SIZE, "10mm").toString() == "2mm"){ - Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE - }else{ - Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM + if(sharedPreference.getString(Constants.CUVETTE_SIZE, "10mm").toString() == "2mm"){ + maxTest = Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE + }else if(sharedPreference.getString(Constants.CUVETTE_SIZE, "10mm").toString() == "10mm"){ + maxTest = Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE10MM + } else if(sharedPreference.getString(Constants.CUVETTE_SIZE, "2mmSt").toString() == "2mmSt"){ + maxTest = Constants.MAXIMUM_TEST_ALLOWED_SINGLE_TEST } val time = timeDifference(sharedPreference.getString(Constants.KIT_TIME, "").toString()) val kitNum = sharedPreference.getString(Constants.KIT_NUMBER, "").toString() @@ -212,19 +224,27 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate { binding.btnGo.setOnClickListener { val serialNumber = binding.nameEditText.text.toString().trim() if (serialNumber.isNotEmpty() && isSerialValid(serialNumber)) { - if(serialNumber.contains("SMI/SC/")){ + if(serialNumber.contains(TEST_FOR_2MM)){ with(sharedPreference.edit()) { putString(Constants.CUVETTE_SIZE, "2mm") apply() } Toast.makeText(this, "Selected cuvette size: 2mm", Toast.LENGTH_SHORT).show() - }else if(serialNumber.contains("SMI/SC-2-D10/")){ + }else if(serialNumber.contains(TEST_FOR_10MM)){ with(sharedPreference.edit()) { putString(Constants.CUVETTE_SIZE, "10mm") apply() } Toast.makeText(this, "Selected cuvette size: 10mm", Toast.LENGTH_SHORT).show() + }else if(serialNumber.contains(singleTest)){ + with(sharedPreference.edit()) { + putString(Constants.CUVETTE_SIZE, "2mmSt") + apply() + } + Toast.makeText(this, "Selected cuvette size: 2mm for single test", Toast.LENGTH_SHORT).show() + binding.nameEditText.setText(serialNumber) } + val kitTime = SimpleDateFormat( "yyyy-MM-dd HH:mm:ss", Locale.getDefault() ).format(Calendar.getInstance().time).toString() @@ -351,16 +371,21 @@ class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate { // } private fun isSerialValid(s: String): Boolean { - if (s.contains("SMI/SC/")) { + if (s.contains(TEST_FOR_2MM)) { if(s.length != 17){ binding.nameEditText.error = "Invalid Kit Serial Number, correct example SMI/SC/000/00/000" return false } - }else if(s.contains("SMI/SC-2-D10/")){ + }else if(s.contains(TEST_FOR_10MM)){ if(s.length != 27){ binding.nameEditText.error = "Invalid Kit Serial Number, correct example SMI/SC-2-D10/000000/000/000" return false } + }else if(s.contains(singleTest)){ + if(s.length != 27){ + binding.nameEditText.error = "Invalid Kit Serial Number, correct example SMI/SC-ST/000000/000/000" + return false + } }else{ return false } diff --git a/app/src/main/java/com/example/hpostesting/presentation/main_base/SettingsFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/main_base/SettingsFragment.kt index dea3a37..0b6d96b 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/main_base/SettingsFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/main_base/SettingsFragment.kt @@ -14,17 +14,23 @@ package com.example.hpostesting.presentation.main_base import android.content.Context +import android.content.DialogInterface import android.content.SharedPreferences import android.content.pm.PackageManager +import android.net.Uri import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.Toast +import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat @@ -33,20 +39,33 @@ import androidx.preference.SwitchPreferenceCompat import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.DataHolder import com.example.hpostesting.data.constant.LanguageManager +import com.example.hpostesting.data.model.patient.HemoCubeTestData import com.example.hpostesting.data.repository.DatabaseRepository import com.example.hpostesting.firebase.FirebaseConfig import com.example.hpostesting.firebase.FirebaseManager +import com.example.hpostesting.presentation.trueheme_test.TrueHemeTestViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.firebase.ktx.Firebase +import com.google.firebase.storage.ktx.storage +import com.google.firebase.storage.ktx.storageMetadata import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.FragmentSlideshowBinding +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale private var isLanguageChanged = false class SlideshowFragment : Fragment() { private var selectedItem = "10mm" - private val values = arrayOf("10mm", "2mm") + private val values = arrayOf("10mm", "2mm", "2mmSt") private lateinit var binding: FragmentSlideshowBinding + private val trueHemeTestViewModel: TrueHemeTestViewModel by activityViewModels() private lateinit var sharedPreferences: SharedPreferences override fun onCreateView( @@ -59,7 +78,9 @@ class SlideshowFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + binding.btnDownload.setOnClickListener { + sendData() + } binding.nameEditText.setText(sharedPreferences.getString(Constants.LABNAME, "")) selectedItem = sharedPreferences.getString(Constants.CUVETTE_SIZE, "10mm").toString() binding.btnGo.setOnClickListener { @@ -81,8 +102,10 @@ class SlideshowFragment : Fragment() { val pos: Int if (selectedItem == "10mm") { pos = 0 - } else { + } else if (selectedItem == "2mm"){ pos = 1 + } else { + pos = 2 } binding.spinnerCuvette.adapter = adapter @@ -114,6 +137,111 @@ class SlideshowFragment : Fragment() { childFragmentManager.beginTransaction().replace(binding.container.id, PrefsFragment()) .commit() } + + private fun sendData() { + //This data will saved in firebase storage check in HPOS-Prod + val dialog = AlertDialog.Builder(requireContext()) + .setTitle("Downloading") + .setMessage("Please wait...") + .setCancelable(false) + .create() + dialog.show() + downloadLocalDBData(dialog) + + } + private fun downloadLocalDBData(dialog: DialogInterface) { + var csvDownloaded = false + + trueHemeTestViewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> + if (!csvDownloaded) { + val downloadList = mutableListOf() + + userDataList.forEach { userData -> + Log.d("DownloadDebug", "userData: $userData, isCSVCreated: ${userData.isCSVCreated}") + downloadList.add(userData) + } + + if (downloadList.isNotEmpty()) { + // Set flag to prevent multiple executions due to observer + csvDownloaded = true + + // Launch coroutine to handle CSV creation and upload + lifecycleScope.launch { + try { + // Call suspend function to create CSV and wait for result + val csvFile = trueHemeTestViewModel.createCSVNew(downloadList, requireContext()) + + if (csvFile != null && csvFile.exists() && csvFile.length() > 0) { + uploadToFirebaseStorage(csvFile) + + Toast.makeText( + requireContext(), + "CSV file created successfully at ${csvFile.absolutePath}", + Toast.LENGTH_LONG + ).show() + } else { + csvDownloaded = false // Reset flag if failed + Toast.makeText( + requireContext(), + "CSV file creation failed, please try again", + Toast.LENGTH_SHORT + ).show() + } + } catch (e: Exception) { + csvDownloaded = false // Reset flag if exception + e.printStackTrace() + Toast.makeText( + requireContext(), + "Error creating CSV file: ${e.message}", + Toast.LENGTH_SHORT + ).show() + } finally { + dialog.dismiss() + } + } + } else { + Toast.makeText(requireContext(), "No data to download", Toast.LENGTH_SHORT).show() + dialog.dismiss() + } + } + } + } + + private fun uploadToFirebaseStorage(file: File) { + // Get Firebase Storage reference + val storage = Firebase.storage + val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) + val storageRef = storage.reference.child("!!!csv_files/hemocube_data_$timestamp.csv") + + // Create file metadata + val metadata = storageMetadata { + contentType = "text/csv" + } + + // Upload file + val uploadTask = storageRef.putFile(Uri.fromFile(file), metadata) + + uploadTask + .addOnSuccessListener { + Toast.makeText( + requireContext(), + "CSV file uploaded to Firebase successfully", + Toast.LENGTH_SHORT + ).show() + } + .addOnFailureListener { exception -> + Log.e("FirebaseUpload", "Upload failed", exception) + Toast.makeText( + requireContext(), + "Failed to upload CSV to Firebase", + Toast.LENGTH_SHORT + ).show() + } + .addOnProgressListener { taskSnapshot -> + val progress = (100.0 * taskSnapshot.bytesTransferred / taskSnapshot.totalByteCount) + Log.d("FirebaseUpload", "Upload is $progress% done") + } + } } class PrefsFragment : PreferenceFragmentCompat() { diff --git a/app/src/main/java/com/example/hpostesting/presentation/trueheme_test/TrueHemeTestFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/trueheme_test/TrueHemeTestFragment.kt index 11ffb7d..b70b7e5 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/trueheme_test/TrueHemeTestFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/trueheme_test/TrueHemeTestFragment.kt @@ -32,6 +32,20 @@ import androidx.lifecycle.MutableLiveData import com.example.hpostesting.data.constant.DataHolder import com.example.hpostesting.util.Result import com.example.hpostesting.data.constant.Constants +import com.example.hpostesting.data.constant.Constants.negativeBoderLineMetricCheck2mmStMax +import com.example.hpostesting.data.constant.Constants.negativeBoderLineMetricCheck2mmStMin +import com.example.hpostesting.data.constant.Constants.negativeBorderlineMaxSt2mm +import com.example.hpostesting.data.constant.Constants.negativeBorderlineMinSt2mm +import com.example.hpostesting.data.constant.Constants.normalMaxSt2mm +import com.example.hpostesting.data.constant.Constants.normalMinSt2mm +import com.example.hpostesting.data.constant.Constants.positiveBoderLineMetricCheck2mmStMax +import com.example.hpostesting.data.constant.Constants.positiveBoderLineMetricCheck2mmStMin +import com.example.hpostesting.data.constant.Constants.positiveForSickleCellMaxSt2mm +import com.example.hpostesting.data.constant.Constants.positiveForSickleCellMinSt2mm +import com.example.hpostesting.data.constant.Constants.sickleCellDiseaseMaxSt2mm +import com.example.hpostesting.data.constant.Constants.sickleCellDiseaseMinSt2mm +import com.example.hpostesting.data.constant.Constants.sickleCellTraitMaxSt2mm +import com.example.hpostesting.data.constant.Constants.sickleCellTraitMinSt2mm import com.example.hpostesting.data.constant.HemoCubeCommands import com.example.hpostesting.data.constant.TestStatus import com.example.hpostesting.data.model.TestState @@ -1196,6 +1210,20 @@ class TrueHemeTestFragment : Fragment() { binding.btnSamplestart.isEnabled = true return } + }else if(cuvetteSizeSP == "2mmSt"){ + inRange2mmLed1 = led1Average in min2mmLed1..max2mmLed1 + inRange2mmLed2 = led2Average in min2mmLed2..max2mmLed2 + if(!inRange2mmLed1 || !inRange2mmLed2){ + trueHemeTestViewModel.messages.postValue("Absorbance out-of-Range - Repeat test with adequate blood volume and Incubation time") + this.testStatusCode = TestStatus.BUFFER_PRINT_COMPLETED.code + binding.testing.visibility = View.GONE + binding.btnPlaceRefreshbuffer.visibility = View.GONE + binding.btnPlacebuffer.visibility = View.GONE + binding.btnSamplestart.visibility = View.VISIBLE + binding.btnSamplestart.isClickable = true + binding.btnSamplestart.isEnabled = true + return + } } } } @@ -1478,6 +1506,25 @@ class TrueHemeTestFragment : Fragment() { return "Positive for Sickle Cell. Confirm with HPLC" } } + }else if(cuvetteSizeSP == "2mmSt"){ + if (deviceRatioClass == "Negative Borderline") { + if (borderlineMetric < negativeBoderLineMetricCheck2mmStMin){//1.34 + return "Sickle Cell Trait" + }else if(borderlineMetric > negativeBoderLineMetricCheck2mmStMax){ + return "Normal" + }else if(borderlineMetric > negativeBoderLineMetricCheck2mmStMin && borderlineMetric < negativeBoderLineMetricCheck2mmStMax){ + return "Negative borderline. Confirm with HPLC" + } + } + if (deviceRatioClass == "Positive for Sickle Cell. HPLC for Confirmation") { + if (borderlineMetric < positiveBoderLineMetricCheck2mmStMin){//1.34 + return "Sickle Cell Disease" + }else if(borderlineMetric > positiveBoderLineMetricCheck2mmStMax){ + return "Sickle Cell Trait" + }else if(borderlineMetric > positiveBoderLineMetricCheck2mmStMin && borderlineMetric < positiveBoderLineMetricCheck2mmStMax){ + return "Positive for Sickle Cell. Confirm with HPLC" + } + } } } } catch (e: Exception) { @@ -1548,6 +1595,22 @@ class TrueHemeTestFragment : Fragment() { if (ratio in sickleCellDiseaseMin2mm..sickleCellDiseaseMax2mm){ return "Sickle Cell Disease" } + }else if(cuvetteSize == "2mmSt"){ + if (ratio in normalMinSt2mm..normalMaxSt2mm) { + return "Normal" + } + if (ratio in negativeBorderlineMinSt2mm..negativeBorderlineMaxSt2mm){ + return "Negative Borderline" + } + if (ratio in sickleCellTraitMinSt2mm..sickleCellTraitMaxSt2mm){ + return "Sickle Cell Trait" + } + if (ratio in positiveForSickleCellMinSt2mm..positiveForSickleCellMaxSt2mm){//0.36 + return "Positive for Sickle Cell. HPLC for Confirmation" + } + if (ratio in sickleCellDiseaseMinSt2mm..sickleCellDiseaseMaxSt2mm){ + return "Sickle Cell Disease" + } } } else { diff --git a/app/src/main/java/com/example/hpostesting/presentation/trueheme_test/TrueHemeTestViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/trueheme_test/TrueHemeTestViewModel.kt index b7c6a44..8c22a4c 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/trueheme_test/TrueHemeTestViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/trueheme_test/TrueHemeTestViewModel.kt @@ -53,11 +53,15 @@ import com.example.hpostesting.util.NetworkMonitor import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import okhttp3.Headers import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.ResponseBody +import java.io.BufferedWriter +import java.io.File +import java.io.FileWriter import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -775,7 +779,44 @@ class TrueHemeTestViewModel @Inject constructor( return maxCapacity } + suspend fun createCSVNew(data: List, context: Context): File? { + return withContext(Dispatchers.IO) { + try { + val timestamp = System.currentTimeMillis() + val fileName = "hemocube_data_$timestamp.csv" + // Create file in app's external files directory + val appFile = File(context.getExternalFilesDir(null), fileName) + + // Ensure this file is used by localFileDataSource + val exportSuccess = localFileDataSource.exportDataToCSV(appFile.absolutePath, data) + + if (exportSuccess) { + // Update database records + data.forEach { item -> + hemoCubeDao.updateCSVFieldById( + item._id, + true + ) + } + + // Verify file exists and has content + if (appFile.exists() && appFile.length() > 0) { + return@withContext appFile + } else { + Log.e("CSVCreation", "File creation verified failed: exists=${appFile.exists()}, size=${appFile.length()}") + return@withContext null + } + } else { + Log.e("CSVCreation", "exportDataToCSV returned false") + return@withContext null + } + } catch (e: Exception) { + Log.e("CSVCreation", "Error creating CSV", e) + return@withContext null + } + } + } fun createCSV(hemoCubeTestData: List, appContext: Context) = viewModelScope.launch { val fileName = "HPOS${getCurrentDate()}.csv" diff --git a/app/src/main/res/layout/fragment_slideshow.xml b/app/src/main/res/layout/fragment_slideshow.xml index 085b234..7376dca 100644 --- a/app/src/main/res/layout/fragment_slideshow.xml +++ b/app/src/main/res/layout/fragment_slideshow.xml @@ -88,12 +88,22 @@ app:layout_constraintBottom_toBottomOf="@id/spinnerCuvette" app:layout_constraintStart_toEndOf="@id/spinnerCuvette" app:layout_constraintTop_toTopOf="@id/spinnerCuvette" /> - +