Skip to content

Commit

Permalink
Merge branch 'main' into fix-loading-nested-list-data
Browse files Browse the repository at this point in the history
  • Loading branch information
dubdabasoduba authored Oct 17, 2024
2 parents 453e16b + f29d85c commit c31686a
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.smartregister.fhircore.engine.configuration.ConfigType
import org.smartregister.fhircore.engine.configuration.Configuration
import org.smartregister.fhircore.engine.configuration.event.EventWorkflow
import org.smartregister.fhircore.engine.domain.model.LauncherType
import org.smartregister.fhircore.engine.util.extension.DEFAULT_FORMAT_SDF_DD_MM_YYYY

@Serializable
data class ApplicationConfiguration(
Expand Down Expand Up @@ -57,6 +58,7 @@ data class ApplicationConfiguration(
id = null,
),
val codingSystems: List<CodingSystemConfig> = emptyList(),
var dateFormat: String = DEFAULT_FORMAT_SDF_DD_MM_YYYY,
) : Configuration()

enum class SyncStrategy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.hl7.fhir.r4.model.DateTimeType
import org.hl7.fhir.r4.model.DateType
import org.ocpsoft.prettytime.PrettyTime
import org.smartregister.fhircore.engine.R
import timber.log.Timber

const val SDF_DD_MMM_YYYY = "dd-MMM-yyyy"
const val SDF_YYYY_MM_DD = "yyyy-MM-dd"
Expand All @@ -39,6 +40,9 @@ const val SDF_YYYY = "yyyy"
const val SDF_D_MMM_YYYY_WITH_COMA = "d MMM, yyyy"
const val SDFHH_MM = "HH:mm"
const val SDF_E_MMM_DD_YYYY = "E, MMM dd yyyy"
const val DEFAULT_FORMAT_SDF_DD_MM_YYYY = "EEE, MMM dd - hh:mm a"
const val SDF_YYYY_MMM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"
const val MMM_D_HH_MM_AA = "MMM d, hh:mm aa"

fun yesterday(): Date = DateTimeType.now().apply { add(Calendar.DATE, -1) }.value

Expand Down Expand Up @@ -149,3 +153,60 @@ fun calculateAge(date: Date, context: Context, localDateNow: LocalDate = LocalDa

private fun Context.abbreviateString(resourceId: Int, content: Int) =
if (content > 0) "$content${this.getString(resourceId).lowercase().abbreviate()} " else ""

fun formatDate(timeMillis: Long, desireFormat: String): String {
return try {
// Try formatting with the provided format
val format = SimpleDateFormat(desireFormat, Locale.getDefault())
val date = Date(timeMillis)
format.format(date)
} catch (e: Exception) {
// If formatting fails, fall back to the default format
val defaultFormat = SimpleDateFormat(DEFAULT_FORMAT_SDF_DD_MM_YYYY, Locale.getDefault())
val date = Date(timeMillis)
defaultFormat.format(date)
}
}

/**
* Reformats a given date string from its current format to a specified desired format. If the date
* string cannot be parsed in the provided current format, the method will return the original date
* string as a fallback.
*
* @param inputDateString The date string that needs to be reformatted.
* @param currentFormat The format in which the input date string is provided (e.g., "dd-MM-yyyy").
* @param desiredFormat The format in which the output date string should be returned (default:
* "yyyy-MM-dd HH:mm:ss"). If no desired format is specified, it defaults to "yyyy-MM-dd
* HH:mm:ss".
* @return A string representing the date in the desired format if parsing succeeds, or the original
* input date string if parsing fails.
*
* Example usage:
* ```
* val reformattedDate = reformatDate("08-10-2024 15:30", "dd-MM-yyyy HH:mm", "yyyy-MM-dd HH:mm:ss")
* println(reformattedDate) // Output: "2024-10-08 15:30:00"
*
* val invalidDate = reformatDate("InvalidDate", "dd-MM-yyyy", "yyyy-MM-dd")
* println(invalidDate) // Output: "InvalidDate"
* ```
*/
fun reformatDate(
inputDateString: String,
currentFormat: String,
desiredFormat: String,
): String {
return try {
// Create a SimpleDateFormat with the current format of the input date
val inputDateFormat = SimpleDateFormat(currentFormat, Locale.getDefault())
// Parse the input date string into a Date object
val date = inputDateFormat.parse(inputDateString)
// Create a SimpleDateFormat for the desired format
val outputDateFormat = SimpleDateFormat(desiredFormat, Locale.getDefault())
// Format the date into the desired format and return the result
outputDateFormat.format(date ?: Date())
} catch (e: Exception) {
// In case of any exception, return the original input date string
Timber.e(e)
inputDateString
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.hl7.fhir.r4.model.AdverseEvent
import org.hl7.fhir.r4.model.Base
import org.hl7.fhir.r4.model.CarePlan
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.Consent
import org.hl7.fhir.r4.model.Encounter
import org.hl7.fhir.r4.model.EpisodeOfCare
import org.hl7.fhir.r4.model.Group
Expand Down Expand Up @@ -85,6 +86,11 @@ class TransformSupportServices @Inject constructor(val simpleWorkerContext: Simp
AdverseEvent.AdverseEventSuspectEntityCausalityComponent()
"Location_Position" -> Location.LocationPositionComponent()
"List_Entry" -> ListResource.ListEntryComponent()
"Consent_Policy" -> Consent.ConsentPolicyComponent()
"Consent_Verification" -> Consent.ConsentVerificationComponent()
"Consent_Provision" -> Consent.provisionComponent()
"Consent_ProvisionActor" -> Consent.provisionActorComponent()
"Consent_ProvisionData" -> Consent.provisionDataComponent()
else -> ResourceFactory.createResourceOrType(name)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,26 @@ class DateTimeExtensionTest : RobolectricTest() {
fun isTodayWithDateYesterdayShouldReturnFalse() {
assertFalse(yesterday().isToday())
}

@Test
fun testReformatDateWithValidDate() {
val inputDateString = "2022-02-02"
val currentFormat = "yyyy-MM-dd"
val desiredFormat = "dd/MM/yyyy"

val result = reformatDate(inputDateString, currentFormat, desiredFormat)

assertEquals("02/02/2022", result)
}

@Test
fun testReformatDateWithInvalidDateFormat() {
val inputDateString = "02/02/2022"
val currentFormat = "yyyy-MM-dd"
val desiredFormat = "dd/MM/yyyy"

val result = reformatDate(inputDateString, currentFormat, desiredFormat)

assertEquals(inputDateString, result)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package org.smartregister.fhircore.engine.util.helper
import io.mockk.mockk
import org.hl7.fhir.exceptions.FHIRException
import org.hl7.fhir.r4.model.CarePlan
import org.hl7.fhir.r4.model.Consent
import org.hl7.fhir.r4.model.Encounter
import org.hl7.fhir.r4.model.EpisodeOfCare
import org.hl7.fhir.r4.model.Group
Expand Down Expand Up @@ -129,6 +130,44 @@ class TransformSupportServicesTest : RobolectricTest() {
)
}

@Test
fun `createType() should return ConsentPolicyComponent when given Consent_Policy`() {
Assert.assertTrue(
transformSupportServices.createType("", "Consent_Policy") is Consent.ConsentPolicyComponent,
)
}

@Test
fun `createType() should return ConsentVerificationComponent when given Consent_Verification`() {
Assert.assertTrue(
transformSupportServices.createType("", "Consent_Verification")
is Consent.ConsentVerificationComponent,
)
}

@Test
fun `createType() should return provisionComponent when given Consent_Provision`() {
Assert.assertTrue(
transformSupportServices.createType("", "Consent_Provision") is Consent.provisionComponent,
)
}

@Test
fun `createType() should return provisionActorComponent when given Consent_ProvisionActor`() {
Assert.assertTrue(
transformSupportServices.createType("", "Consent_ProvisionActor")
is Consent.provisionActorComponent,
)
}

@Test
fun `createType() should return provisionDataComponent when given Consent_ProvisionData`() {
Assert.assertTrue(
transformSupportServices.createType("", "Consent_ProvisionData")
is Consent.provisionDataComponent,
)
}

@Test
fun `createType() should return Time when given time`() {
Assert.assertTrue(transformSupportServices.createType("", "time") is TimeType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.smartregister.fhircore.engine.util.extension.DEFAULT_FORMAT_SDF_DD_MM_YYYY
import org.smartregister.fhircore.quest.ui.usersetting.INSIGHT_UNSYNCED_DATA
import org.smartregister.fhircore.quest.ui.usersetting.UserSettingInsightScreen

Expand Down Expand Up @@ -118,6 +119,7 @@ class UserSettingInsightScreenTest {
unsyncedResourcesFlow = unsyncedResourcesFlow,
navController = rememberNavController(),
onRefreshRequest = {},
dateFormat = DEFAULT_FORMAT_SDF_DD_MM_YYYY,
)
}
this.activity = activity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,6 @@
],
"logGpsLocation": [
"QUESTIONNAIRE"
]
],
"dateFormat": "MMM d, hh:mm aa"
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
import org.smartregister.fhircore.engine.util.extension.countUnSyncedResources
import org.smartregister.fhircore.engine.util.extension.extractLogicalIdUuid
import org.smartregister.fhircore.engine.util.extension.fetchLanguages
import org.smartregister.fhircore.engine.util.extension.formatDate
import org.smartregister.fhircore.engine.util.extension.getActivity
import org.smartregister.fhircore.engine.util.extension.isDeviceOnline
import org.smartregister.fhircore.engine.util.extension.reformatDate
import org.smartregister.fhircore.engine.util.extension.refresh
import org.smartregister.fhircore.engine.util.extension.setAppLocale
import org.smartregister.fhircore.engine.util.extension.showToast
Expand Down Expand Up @@ -138,7 +140,7 @@ constructor(
appTitle = applicationConfiguration.appTitle,
currentLanguage = loadCurrentLanguage(),
username = secureSharedPreference.retrieveSessionUsername() ?: "",
lastSyncTime = retrieveLastSyncTimestamp() ?: "",
lastSyncTime = getSyncTime(),
languages = configurationRegistry.fetchLanguages(),
navigationConfiguration = navigationConfiguration,
registerCountMap = registerCountMap,
Expand All @@ -147,6 +149,47 @@ constructor(

countRegisterData()
}
// todo - if we can move this method to somewhere else where it can be accessed easily on multiple
// view models
/**
* Retrieves the last sync time from shared preferences and returns it in a formatted way. This
* method handles both cases:
* 1. The time stored as a timestamp in milliseconds (preferred).
* 2. Backward compatibility where the time is stored in a formatted string.
*
* @return A formatted sync time string.
*/
fun getSyncTime(): String {
var result = ""

// First, check if we have any previously stored sync time in SharedPreferences.
retrieveLastSyncTimestamp()?.let { storedDate ->

// Try to treat the stored time as a timestamp (in milliseconds).
runCatching {
// Attempt to convert the stored date to Long (i.e., millis format) and format it.
result =
formatDate(
timeMillis = storedDate.toLong(),
desireFormat = applicationConfiguration.dateFormat,
)
}
.onFailure {
// If conversion to Long fails, it's likely that the stored date is in a formatted string
// (backward compatibility).
// Reformat the stored date using the provided SYNC_TIMESTAMP_OUTPUT_FORMAT.
result =
reformatDate(
inputDateString = storedDate,
currentFormat = SYNC_TIMESTAMP_OUTPUT_FORMAT,
desiredFormat = applicationConfiguration.dateFormat,
)
}
}

// Return the result (either formatted time in millis or re-formatted backward-compatible date).
return result
}

fun countRegisterData() {
viewModelScope.launch {
Expand Down Expand Up @@ -186,7 +229,7 @@ constructor(
if (event.state is CurrentSyncJobStatus.Succeeded) {
sharedPreferencesHelper.write(
SharedPreferenceKey.LAST_SYNC_TIMESTAMP.name,
formatLastSyncTimestamp(event.state.timestamp),
event.state.timestamp.toInstant().toEpochMilli().toString(),
)
retrieveAppMainUiState()
viewModelScope.launch { retrieveAppMainUiState() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ class UserInsightScreenFragment : Fragment() {
location = userSettingViewModel.practitionerLocation(),
appVersionCode = userSettingViewModel.appVersionCode.toString(),
appVersion = userSettingViewModel.appVersionName,
buildDate = userSettingViewModel.buildDate,
buildDate = userSettingViewModel.getBuildDate(),
unsyncedResourcesFlow = userSettingViewModel.unsyncedResourcesMutableSharedFlow,
navController = findNavController(),
onRefreshRequest = { userSettingViewModel.fetchUnsyncedResources() },
dateFormat = userSettingViewModel.getDateFormat(),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class UserSettingFragment : Fragment(), OnSyncListener {
userSettingViewModel.progressBarState.observeAsState(Pair(false, 0)).value,
isDebugVariant = BuildConfig.DEBUG,
mainNavController = findNavController(),
lastSyncTime = userSettingViewModel.retrieveLastSyncTimestamp(),
lastSyncTime = appMainViewModel.getSyncTime(),
showProgressIndicatorFlow = userSettingViewModel.showProgressIndicatorFlow,
dataMigrationVersion = userSettingViewModel.retrieveDataMigrationVersion(),
enableManualSync =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import org.smartregister.fhircore.engine.R
import org.smartregister.fhircore.engine.ui.theme.DividerColor
import org.smartregister.fhircore.engine.ui.theme.LoginDarkColor
import org.smartregister.fhircore.engine.util.extension.DEFAULT_FORMAT_SDF_DD_MM_YYYY
import org.smartregister.fhircore.engine.util.extension.formatDate

const val USER_INSIGHT_TOP_APP_BAR = "userInsightToAppBar"
const val INSIGHT_UNSYNCED_DATA = "insightUnsyncedData"
Expand All @@ -93,6 +95,7 @@ fun UserSettingInsightScreen(
unsyncedResourcesFlow: MutableSharedFlow<List<Pair<String, Int>>>,
navController: NavController,
onRefreshRequest: () -> Unit,
dateFormat: String = DEFAULT_FORMAT_SDF_DD_MM_YYYY,
) {
val unsyncedResources = unsyncedResourcesFlow.collectAsState(initial = listOf()).value

Expand Down Expand Up @@ -235,7 +238,8 @@ fun UserSettingInsightScreen(
(if (Build.DEVICE.isNullOrEmpty()) "-" else Build.DEVICE),
stringResource(R.string.os_version) to
(if (Build.VERSION.BASE_OS.isNullOrEmpty()) "-" else Build.VERSION.BASE_OS),
stringResource(R.string.device_date) to (formatTimestamp(Build.TIME).ifEmpty { "-" }),
stringResource(R.string.device_date) to
(formatDate(Build.TIME, desireFormat = dateFormat).ifEmpty { "-" }),
)
InsightInfoView(
title = stringResource(id = R.string.device_info),
Expand Down Expand Up @@ -365,6 +369,7 @@ fun UserSettingInsightScreenPreview() {
unsyncedResourcesFlow = MutableSharedFlow(),
navController = rememberNavController(),
onRefreshRequest = {},
dateFormat = DEFAULT_FORMAT_SDF_DD_MM_YYYY,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ import org.smartregister.fhircore.engine.util.DispatcherProvider
import org.smartregister.fhircore.engine.util.SecureSharedPreference
import org.smartregister.fhircore.engine.util.SharedPreferenceKey
import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
import org.smartregister.fhircore.engine.util.extension.SDF_YYYY_MMM_DD_HH_MM_SS
import org.smartregister.fhircore.engine.util.extension.countUnSyncedResources
import org.smartregister.fhircore.engine.util.extension.fetchLanguages
import org.smartregister.fhircore.engine.util.extension.getActivity
import org.smartregister.fhircore.engine.util.extension.isDeviceOnline
import org.smartregister.fhircore.engine.util.extension.launchActivityWithNoBackStackHistory
import org.smartregister.fhircore.engine.util.extension.reformatDate
import org.smartregister.fhircore.engine.util.extension.refresh
import org.smartregister.fhircore.engine.util.extension.setAppLocale
import org.smartregister.fhircore.engine.util.extension.showToast
Expand Down Expand Up @@ -89,7 +91,6 @@ constructor(

val appVersionCode = BuildConfig.VERSION_CODE
val appVersionName = BuildConfig.VERSION_NAME
val buildDate = BuildConfig.BUILD_DATE

fun retrieveUsername(): String? = secureSharedPreference.retrieveSessionUsername()

Expand Down Expand Up @@ -204,6 +205,15 @@ constructor(

fun enabledDeviceToDeviceSync(): Boolean = applicationConfiguration.deviceToDeviceSync != null

fun getDateFormat() = applicationConfiguration.dateFormat

fun getBuildDate() =
reformatDate(
inputDateString = BuildConfig.BUILD_DATE,
currentFormat = SDF_YYYY_MMM_DD_HH_MM_SS,
desiredFormat = applicationConfiguration.dateFormat,
)

fun fetchUnsyncedResources() {
viewModelScope.launch {
withContext(dispatcherProvider.io()) {
Expand Down
Loading

0 comments on commit c31686a

Please sign in to comment.