CSV Download

This commit is contained in:
Kaif
2023-12-14 13:59:48 +05:30
parent e7600dafec
commit 75875b9e4f
15 changed files with 485 additions and 245 deletions

View File

@@ -103,7 +103,7 @@ dependencies {
implementation "androidx.fragment:fragment-ktx:1.6.2"
// CSV read, write
implementation 'com.opencsv:opencsv:4.6'
implementation 'com.opencsv:opencsv:5.9'
// Barcode scanner
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'

View File

@@ -23,4 +23,7 @@ interface HemoCubeDao {
@Query("UPDATE hemo_cube_test_table SET localFlag = :newValue WHERE _id = :id")
suspend fun updateFieldById(id: String, newValue: Boolean)
@Query("UPDATE hemo_cube_test_table SET isCSVCreated = :newValue WHERE _id = :id")
suspend fun updateCSVFieldById(id: String, newValue: Boolean)
}

View File

@@ -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 = 11, exportSchema = false)
@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class], version = 12, exportSchema = false)
@TypeConverters(Converters::class)
abstract class MyDatabase : RoomDatabase() {
abstract fun userDao(): UserDao

View File

@@ -1,8 +1,11 @@
package com.example.hpostesting.data.datasource
import android.os.Environment
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.opencsv.CSVWriter
import java.io.File
import java.io.FileWriter
import java.io.IOException
class LocalFileDataSource {
@@ -20,4 +23,138 @@ class LocalFileDataSource {
writer.close()
}
fun exportDataToCSV(
fileName: String, dataList: List<HemoCubeTestData>
): 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)
val csvWriter = CSVWriter(writer)
// Write CSV header
val header = arrayOf(
"_id",
"name",
"incubationTime",
"bloodGroup",
"birthYear", // Include other fields from the data class
"state",
"abhaId",
"userImageURL",
"location",
"reportUploadTime",
"testType",
"testTime",
"testStatus",
"gender",
"localFlag", // Add more fields as necessary
"deviceId",
"appVersion",
"deviceSerialNumber",
"deviceType",
"kitSerial",
"resultData",
"led1Buffer",
"led2Buffer",
"led3Buffer",
"led4Buffer",
"led1Sample",
"led2Sample",
"led3Sample",
"led4Sample",
"led1Average",
"led2Average",
"led3Average",
"led4Average",
"abs1",
"abs2",
"abs3",
"abs4",
"deviceRatio",
"calculatedRatio",
"predictedDenovixRatio",
"coefficients",
"classificationResult",
"prdClassification",
"errorMessages",
"batteryLevel",
"batteryCapacity",
"batteryMaxCapacity",
"batteryTemperature",
"batteryVoltage"
)
csvWriter.writeNext(header)
// Write data rows
for (data in dataList) {
val row = arrayOf(
data._id,
data.name,
data.incubationTime,
data.bloodGroup,
data.birthYear, // Include other fields similarly
data.state,
data.abhaId,
data.userImageURL,
data.location?.toString(),
data.reportUploadTime ?: "",
data.testType ?: "",
data.testTime ?: "",
data.testStatus?.toString() ?: "",
data.gender,
data.localFlag.toString(),
data.deviceId ?: "",
data.appVersion ?: "",
data.deviceSerialNumber,
data.deviceType,
data.kitSerial,
data.resultData,
data.led1Buffer?.toString() ?: "",
data.led2Buffer?.toString() ?: "",
data.led3Buffer?.toString() ?: "",
data.led4Buffer?.toString() ?: "",
data.led1Sample?.toString() ?: "",
data.led2Sample?.toString() ?: "",
data.led3Sample?.toString() ?: "",
data.led4Sample?.toString() ?: "",
data.led1Average?.toString() ?: "",
data.led2Average?.toString() ?: "",
data.led3Average?.toString() ?: "",
data.led4Average?.toString() ?: "",
data.abs1?.toString() ?: "",
data.abs2?.toString() ?: "",
data.abs3?.toString() ?: "",
data.abs4?.toString() ?: "",
data.deviceRatio?.toString() ?: "",
data.calculatedRatio?.toString() ?: "",
data.predictedDenovixRatio?.toString() ?: "",
data.coefficients ?: "",
data.classificationResult,
data.prdClassification,
data.errorMessages,
data.batteryLevel,
data.batteryCapacity,
data.batteryMaxCapacity,
data.batteryTemperature,
data.batteryVoltage
)
csvWriter.writeNext(row)
}
writer.close()
return true
} catch (e: IOException) {
e.printStackTrace()
return false
}
}
private fun getExternalStorageDirectory(): File {
val folder = File(Environment.getExternalStorageDirectory(), "HposFolder")
if (!folder.exists()) {
folder.mkdirs()
}
return folder
}
}

View File

@@ -55,4 +55,5 @@ data class HemoCubeTestData(
var batteryMaxCapacity: String = "",
var batteryTemperature: String = "",
var batteryVoltage: String = "",
var isCSVCreated: Boolean = false
)

View File

@@ -28,9 +28,7 @@ object AppModule {
fun provideMyDatabase(@ApplicationContext context: Context): MyDatabase {
return Room.databaseBuilder(
context, MyDatabase::class.java, "my_database"
)
.fallbackToDestructiveMigration()
.build()
).fallbackToDestructiveMigration().build()
}
@Provides
@@ -76,4 +74,10 @@ object AppModule {
fun provideRepository(): Repository {
return DatabaseRepository()
}
@Provides
@Singleton
fun provideLocalFileDataSource(): LocalFileDataSource {
return LocalFileDataSource()
}
}

View File

@@ -1,6 +1,7 @@
package com.example.hpostesting.presentation.adapter
import android.content.Context
import android.content.res.Resources
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -64,7 +65,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int)
if (batLevel < 40) {
Toast.makeText(
view.context,
context?.getString(R.string.low_battery_warning),
R.string.low_battery_warning,
Toast.LENGTH_SHORT
).show()
return@setOnClickListener
@@ -73,7 +74,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int)
if (userList.testStatus!!) {
Toast.makeText(
view.context,
context?.getString(R.string.test_already_conducted),
R.string.test_already_conducted,
Toast.LENGTH_SHORT
).show()
} else {
@@ -81,13 +82,13 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int)
if (isBetween15And30Minutes(userList.incubationTime) < Constants.INCUBATION_TIME_MIN) {
Toast.makeText(
view.context,
context?.getString(R.string.incubation_not_completed),
R.string.incubation_not_completed,
Toast.LENGTH_SHORT
).show()
} else if (isBetween15And30Minutes(userList.incubationTime) > Constants.INCUBATION_TIME_MAX) {
Toast.makeText(
view.context,
context?.getString(R.string.incubation_crossed_30_minutes),
R.string.incubation_crossed_30_minutes,
Toast.LENGTH_SHORT
).show()
} else {
@@ -102,7 +103,7 @@ class OfflineUserListAdapter(private val view: View, private val batLevel: Int)
} else {
Toast.makeText(
view.context,
context?.getString(R.string.incubation_not_started),
R.string.incubation_not_started,
Toast.LENGTH_SHORT
).show()
}

View File

@@ -213,13 +213,13 @@ class UserListAdapter(
).show()
} else {
if (model.incubationTime != "") {
if (isBetween15And30Minutes(model.incubationTime) < 15) {
if (isBetween15And30Minutes(model.incubationTime) < 0) {
Toast.makeText(
view.context,
context?.getString(R.string.incubation_not_completed),
Toast.LENGTH_SHORT
).show()
} else if (isBetween15And30Minutes(model.incubationTime) > 30) {
} else if (isBetween15And30Minutes(model.incubationTime) > 140000) {
Toast.makeText(
view.context,
context?.getString(R.string.incubation_crossed_30_minutes),

View File

@@ -8,6 +8,7 @@ import android.content.Intent
import android.content.SharedPreferences
import android.os.BatteryManager
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -72,6 +73,7 @@ class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
checkUnprocessedCSVData()
viewModel.allUserData.observe(viewLifecycleOwner) { userData ->
deleteIncompleteRegistrations(userData)
}
@@ -107,9 +109,7 @@ class HomeFragment : Fragment() {
hemoCubeViewModel.fireBaseBulkUpload.observe(viewLifecycleOwner) { result ->
if (result == "Success") {
Toast.makeText(
requireContext(),
R.string.test_upload,
Toast.LENGTH_SHORT
requireContext(), R.string.test_upload, Toast.LENGTH_SHORT
).show()
}
if (result == "Error") {
@@ -132,6 +132,9 @@ class HomeFragment : Fragment() {
startActivity(Intent(requireContext(), KitScanActivity::class.java))
requireActivity().finish()
}
binding.downloadCSV.setOnClickListener {
showDownloadDialog(requireContext())
}
}
private fun setUserId() {
@@ -141,7 +144,9 @@ class HomeFragment : Fragment() {
if (userId.length >= 18 && !bloodGroup.equals("Select Blood Group") || !bloodGroup.isNullOrBlank()) {
hemoCubeViewModel.addUser(
HemoCubeTestData(
_id = userId, bloodGroup = bloodGroup.toString(), incubationTime = SimpleDateFormat(
_id = userId,
bloodGroup = bloodGroup.toString(),
incubationTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time).toString()
)
@@ -182,8 +187,12 @@ class HomeFragment : Fragment() {
rvAdapter = view?.let {
UserListAdapter(
requireContext(), hemoCubeViewModel, recyclerViewOptions,
it, batLevel, requireActivity()
requireContext(),
hemoCubeViewModel,
recyclerViewOptions,
it,
batLevel,
requireActivity()
)
}!!
binding.rvOrder.adapter = rvAdapter
@@ -313,6 +322,14 @@ class HomeFragment : Fragment() {
}
}
private fun checkUnprocessedCSVData() {
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
val downloadDataVisibility =
if (userDataList.any { !it.isCSVCreated && it.testStatus == true }) View.VISIBLE else View.GONE
binding.downloadCSV.visibility = downloadDataVisibility
}
}
private fun showUploadDialog(context: Context) {
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.upload_db_registration_title)
@@ -330,6 +347,23 @@ class HomeFragment : Fragment() {
dialog.show()
}
private fun showDownloadDialog(context: Context) {
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.download_db_registration_title)
builder.setMessage(R.string.download_db_registration_message)
builder.setPositiveButton(R.string.downloadcsv) { dialog, _ ->
downloadLocalDBData(dialog)
}
builder.setNegativeButton(R.string.cancel) { dialog, _ ->
dialog.dismiss()
}
val dialog = builder.create()
dialog.show()
}
private fun uploadLocalDBData(dialog: DialogInterface) {
viewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
if (userDataList.isEmpty()) {
@@ -357,6 +391,28 @@ class HomeFragment : Fragment() {
}
}
private fun downloadLocalDBData(dialog: DialogInterface) {
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
val downloadList = mutableListOf<HemoCubeTestData>()
userDataList.forEach { userData ->
if (!userData.isCSVCreated) {
downloadList.add(userData) // Add the userData to downloadList
}
}
if (downloadList.isNotEmpty()) {
// Call ViewModel function to create CSV with filtered data
hemoCubeViewModel.createCSV(downloadList, requireContext())
Toast.makeText(requireContext(), "Done", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(requireContext(), "Failed", Toast.LENGTH_SHORT).show()
}
dialog.dismiss()
}
}
private fun deleteIncompleteRegistrations(userDataList: List<UserData>) {
userDataList.forEach { userData ->
if (userData.csvPath.isEmpty()) {

View File

@@ -1,13 +1,11 @@
package com.example.hpostesting.presentation.hemocube
import android.app.Application
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.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -16,13 +14,12 @@ import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.NetworkStatusLiveData
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.datasource.LocalFileDataSource
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
@@ -36,6 +33,7 @@ class HemoCubeViewModel @Inject constructor(
private val hemoCubeDao: HemoCubeDao,
private val repository: Repository,
context: Context,
private val localFileDataSource: LocalFileDataSource
) : ViewModel() {
var isServiceConnected = false
val progressBar = MutableLiveData(false)
@@ -76,9 +74,7 @@ class HemoCubeViewModel @Inject constructor(
addResultTestToDb()
} else {
parseData()
testDetails?.testTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
testDetails?.testTime = getCurrentDate()
hemoCubeDao.insertAll(testDetails!!)
fireBaseUpload.postValue("Local")
}
@@ -101,9 +97,7 @@ class HemoCubeViewModel @Inject constructor(
testDetails?.deviceRatio = DataHolder.hemocubeResult
testDetails?.resultData = DataHolder.hemoCubeTestData?.resultData.toString()
testDetails?.location = DataHolder.location
testDetails?.testTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
testDetails?.testTime = getCurrentDate()
testDetails?.appVersion = DataHolder.hemoCubeTestData?.appVersion
testDetails?.deviceId = DataHolder.hemoCubeTestData?.deviceId
testDetails?.deviceSerialNumber =
@@ -144,9 +138,7 @@ class HemoCubeViewModel @Inject constructor(
private fun addResultTestToDb() {
viewModelScope.launch {
try {
testDetails!!.reportUploadTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
testDetails!!.reportUploadTime = getCurrentDate()
when (val response = repository.addTestToDatabase(testDetails)) {
is Response.Success -> {
@@ -200,9 +192,7 @@ class HemoCubeViewModel @Inject constructor(
fun bulkAddResultTestToDb(userData: HemoCubeTestData) {
viewModelScope.launch {
userData.reportUploadTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
userData.reportUploadTime = getCurrentDate()
when (repository.addTestToDatabase(userData)) {
is Response.Success -> {
fireBaseBulkUpload.postValue("Success")
@@ -240,7 +230,7 @@ class HemoCubeViewModel @Inject constructor(
fun getBatteryTemperature(): Float? {
val batteryTemp: Float? = batteryStatus?.let { intent ->
val temperature = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0
val temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0)
temperature.toFloat() / 10
}
@@ -276,4 +266,24 @@ class HemoCubeViewModel @Inject constructor(
return maxCapacity
}
fun createCSV(hemoCubeTestData: List<HemoCubeTestData>, appContext: Context) =
viewModelScope.launch {
val fileName = "HPOS${getCurrentDate()}.csv"
if (localFileDataSource.exportDataToCSV(fileName, hemoCubeTestData)) {
hemoCubeTestData.forEach { data ->
data.localFlag = true
hemoCubeDao.updateCSVFieldById(
data._id,
true
)
}
}
}
private fun getCurrentDate(): String {
return SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
}
}

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

@@ -18,11 +18,24 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/downloadCSV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="32dp"
android:visibility="gone"
android:backgroundTint="@color/primary"
android:contentDescription="@string/downloadcsv"
android:src="@drawable/download_csv"
app:elevation="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/internetNotAvailableCL"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
android:visibility="gone"
android:padding="24dp">
<TextView
@@ -31,6 +44,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="24dp"
android:text="@string/internet_not_available_please_enter_the_user_id_manually"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -106,7 +120,7 @@
android:id="@+id/internetAvailableCL"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:visibility="visible"
android:padding="24dp">
<TextView

View File

@@ -185,6 +185,8 @@
<string name="user_id_error_message">उपयोगकर्ता आईडी 18 अंकों की होनी चाहिए और कृपया रक्त समूह का चयन करें</string>
<string name="upload_db_registration_title">डीबी टेस्ट अपलोड करें</string>
<string name="upload_db_registration_message">क्या आप स्थानीय डीबी टेस्ट को क्लाउड पर अपलोड करना चाहते हैं?</string>
<string name="download_db_registration_title">डाउनलोड डीबी टेस्ट</string>
<string name="download_db_registration_message">क्या आप लोकल डीबी टेस्ट्स को सीएसवी प्रारूप में डाउनलोड करना चाहते हैं?</string>
<string name="upload">अपलोड</string>
<string name="cancel">रद्द करें</string>
<string name="upload_success_message">अपलोड सफलता पूर्वक हुआ</string>
@@ -260,4 +262,5 @@
<string name="app_language">ऐप भाषा</string>
<string name="add_blood_group">रक्त समूह जोड़ें</string>
<string name="ok">ठीक है</string>
<string name="downloadcsv">सीएसवी डाउनलोड करें</string>
</resources>

View File

@@ -184,6 +184,8 @@
<string name="user_id_error_message">ಬಳಕೆದಾರ ಐಡಿ 18 ಅಂಕಗಳಿರಬೇಕು ಮತ್ತು ದಯವಿಟ್ಟು ರಕ್ತ ಗುಂಪುವನ್ನು ಆಯ್ಕೆ ಮಾಡಿ</string>
<string name="upload_db_registration_title">ಸ್ಥಾನಿಕ DB ಪರೀಕ್ಷೆಗಳನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಿ</string>
<string name="upload_db_registration_message">ನೀವು ಸ್ಥಾನಿಕ DB ಪರೀಕ್ಷೆಗಳನ್ನು ಕ್ಲೌಡ್‌ಗೆ ಅಪ್ಲೋಡ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಾ?</string>
<string name="download_db_registration_title">ಡೌನ್ಲೋಡ್ ಡಿಬಿ ಟೆಸ್ಟ್‌ಗಳು</string>
<string name="download_db_registration_message">ನೀವು ಲೋಕಲ್ ಡಿಬಿ ಟೆಸ್ಟ್‌ಗಳನ್ನು CSV ಆಕರದಲ್ಲಿ ಡೌನ್ಲೋಡ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಾ?</string>
<string name="upload">ಅಪ್ಲೋಡ್ ಮಾಡಿ</string>
<string name="cancel">ರದ್ದು ಮಾಡಿ</string>
<string name="upload_success_message">ಯಶಸ್ವಿಯಾಗಿ ಅಪ್ಲೋಡ್ ಮಾಡಲಾಗಿದೆ</string>
@@ -260,6 +262,7 @@
<string name="app_language">ಅಪ್ಲಿಕೇಶನ್ ಭಾಷೆ</string>
<string name="add_blood_group">ರಕ್ತ ಗುಂಡನ್ನು ಸೇರಿಸಿ</string>
<string name="ok">ಸರಿ</string>
<string name="downloadcsv">CSV ಡೌನ್ಲೋಡ್ ಮಾಡಿ</string>
<!-- Add translations for other strings -->
</resources>

View File

@@ -186,6 +186,8 @@
<string name="user_id_error_message">User ID should be 18 digits and please select the blood group</string>
<string name="upload_db_registration_title">Upload DB Tests</string>
<string name="upload_db_registration_message">Do you want to upload the local DB tests to the cloud?</string>
<string name="download_db_registration_title">Download DB Tests</string>
<string name="download_db_registration_message">Do you want to download the local DB tests in CSV format?</string>
<string name="upload">Upload</string>
<string name="cancel">Cancel</string>
<string name="upload_success_message">Upload done successfully</string>
@@ -260,4 +262,5 @@
<string name="select_language">Select your preferred language</string>
<string name="app_language">App Language</string>
<string name="ok">OK</string>
<string name="downloadcsv">Download CSV</string>
</resources>