From 216c96ccc7d63d9e44d223dde40df76d77e16c70 Mon Sep 17 00:00:00 2001 From: "S. Grimault" Date: Sat, 17 Jun 2023 19:16:45 +0200 Subject: [PATCH] feat(#122): additional fields data synchronization is optional --- README.md | 3 +- gn_mobile_core | 2 +- .../nomenclature/NomenclatureModule.kt | 21 -- .../presentation/NomenclatureViewModel.kt | 2 + ...ynchronizeAdditionalFieldRepositoryImpl.kt | 76 ----- .../usecase/GetEditableFieldsUseCase.kt | 6 +- .../ObservationRecordViewModel.kt | 11 +- .../usecase/EditObservationRecordUseCase.kt | 12 +- .../SetDefaultNomenclatureValuesUseCase.kt | 13 +- .../occtax/settings/NomenclatureSettings.kt | 10 + .../io/OnAppSettingsJsonReaderListenerImpl.kt | 5 + .../geonature/occtax/ui/home/HomeActivity.kt | 8 + .../ui/input/InputPagerFragmentActivity.kt | 4 + .../ui/input/counting/CountingFragment.kt | 10 + .../counting/EditCountingMetadataActivity.kt | 10 + .../counting/EditCountingMetadataFragment.kt | 10 + .../input/informations/InformationFragment.kt | 10 + occtax/src/main/res/values-fr/strings.xml | 2 - occtax/src/main/res/values/strings.xml | 2 - .../usecase/GetEditableFieldsUseCaseTest.kt | 151 +++++++++ ...SetDefaultNomenclatureValuesUseCaseTest.kt | 296 +++++++++++++++++- .../settings/io/AppSettingsJsonReaderTest.kt | 1 + .../resources/fixtures/settings_occtax.json | 1 + 23 files changed, 550 insertions(+), 116 deletions(-) delete mode 100644 occtax/src/main/java/fr/geonature/occtax/features/nomenclature/repository/SynchronizeAdditionalFieldRepositoryImpl.kt diff --git a/README.md b/README.md index 11815412..caa8f99b 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Example: ### Parameters description | Parameter | UI | Description | Default value | -| ---------------------------------- | ------- | -------------------------------------------------------------------------------------------------- | ------------- | +|------------------------------------| ------- |----------------------------------------------------------------------------------------------------| ------------- | | `area_observation_duration` | ☐ | Area observation duration period (in days) | 365 | | `sync` | ☐ | Data synchronization settings (cf. https://github.com/PnX-SI/gn_mobile_core/tree/develop/datasync) | | | `map` | ☐ | Maps settings (cf. https://github.com/PnX-SI/gn_mobile_maps/tree/develop/maps) | | @@ -107,6 +107,7 @@ Example: | `input/date` | ☐ | Date settings | | | `nomenclature` | ☐ | Nomenclature settings | | | `nomenclature/save_default_values` | ☐ | Save default nomenclature values | false | +| `nomenclature/additional_fields` | ☐ | Show additional fields | false | | `nomenclature/information` | ☐ | Information settings (as array) | | | `nomenclature/counting` | ☐ | Counting settings (as array) | | diff --git a/gn_mobile_core b/gn_mobile_core index 2b5ad46b..95fde331 160000 --- a/gn_mobile_core +++ b/gn_mobile_core @@ -1 +1 @@ -Subproject commit 2b5ad46bf17904dca7066d1400ce3df6fba0e2c6 +Subproject commit 95fde331680149c473fc3a0362917258a87f55b3 diff --git a/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/NomenclatureModule.kt b/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/NomenclatureModule.kt index b0a197b3..69560139 100644 --- a/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/NomenclatureModule.kt +++ b/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/NomenclatureModule.kt @@ -1,10 +1,8 @@ package fr.geonature.occtax.features.nomenclature -import android.content.Context import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import fr.geonature.commons.data.GeoNatureModuleName import fr.geonature.commons.data.LocalDatabase @@ -14,8 +12,6 @@ import fr.geonature.commons.features.nomenclature.data.AdditionalFieldLocalDataS import fr.geonature.commons.features.nomenclature.data.IAdditionalFieldLocalDataSource import fr.geonature.commons.features.nomenclature.data.INomenclatureLocalDataSource import fr.geonature.commons.features.nomenclature.data.NomenclatureLocalDataSourceImpl -import fr.geonature.datasync.api.IGeoNatureAPIClient -import fr.geonature.datasync.sync.repository.ISynchronizeAdditionalDataRepository import fr.geonature.occtax.features.nomenclature.data.INomenclatureSettingsLocalDataSource import fr.geonature.occtax.features.nomenclature.data.IPropertyValueLocalDataSource import fr.geonature.occtax.features.nomenclature.data.InMemoryPropertyValueLocalDataSourceImpl @@ -26,7 +22,6 @@ import fr.geonature.occtax.features.nomenclature.repository.IAdditionalFieldRepo import fr.geonature.occtax.features.nomenclature.repository.IDefaultPropertyValueRepository import fr.geonature.occtax.features.nomenclature.repository.INomenclatureRepository import fr.geonature.occtax.features.nomenclature.repository.NomenclatureRepositoryImpl -import fr.geonature.occtax.features.nomenclature.repository.SynchronizeAdditionalFieldRepositoryImpl import javax.inject.Singleton /** @@ -107,20 +102,4 @@ object NomenclatureModule { ): IAdditionalFieldRepository { return AdditionalFieldRepositoryImpl(additionalFieldLocalDataSource) } - - @Singleton - @Provides - fun provideSynchronizeAdditionalFieldRepository( - @ApplicationContext appContext: Context, - @GeoNatureModuleName moduleName: String, - additionalFieldLocalDataSource: IAdditionalFieldLocalDataSource, - geoNatureAPIClient: IGeoNatureAPIClient, - ): ISynchronizeAdditionalDataRepository { - return SynchronizeAdditionalFieldRepositoryImpl( - appContext, - moduleName, - additionalFieldLocalDataSource, - geoNatureAPIClient - ) - } } \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/presentation/NomenclatureViewModel.kt b/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/presentation/NomenclatureViewModel.kt index c64572c0..1f80cb99 100644 --- a/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/presentation/NomenclatureViewModel.kt +++ b/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/presentation/NomenclatureViewModel.kt @@ -39,6 +39,7 @@ class NomenclatureViewModel @Inject constructor( */ fun getEditableFields( datasetId: Long? = null, + withAdditionalFields: Boolean = false, type: EditableField.Type, defaultPropertySettings: List = listOf(), taxonomy: Taxonomy? = null @@ -46,6 +47,7 @@ class NomenclatureViewModel @Inject constructor( getEditableFieldsUseCase( GetEditableFieldsUseCase.Params( datasetId, + withAdditionalFields, type, defaultPropertySettings, taxonomy diff --git a/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/repository/SynchronizeAdditionalFieldRepositoryImpl.kt b/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/repository/SynchronizeAdditionalFieldRepositoryImpl.kt deleted file mode 100644 index e19bb42c..00000000 --- a/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/repository/SynchronizeAdditionalFieldRepositoryImpl.kt +++ /dev/null @@ -1,76 +0,0 @@ -package fr.geonature.occtax.features.nomenclature.repository - -import android.content.Context -import androidx.work.WorkInfo -import fr.geonature.commons.features.nomenclature.data.IAdditionalFieldLocalDataSource -import fr.geonature.datasync.api.IGeoNatureAPIClient -import fr.geonature.datasync.sync.DataSyncStatus -import fr.geonature.datasync.sync.io.AdditionalFieldJsonReader -import fr.geonature.datasync.sync.repository.ISynchronizeAdditionalDataRepository -import fr.geonature.occtax.R -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow -import org.tinylog.Logger -import retrofit2.await -import java.io.BufferedReader - -/** - * Default implementation of [ISynchronizeAdditionalDataRepository] to synchronize additional fields. - * - * @author S. Grimault - */ -class SynchronizeAdditionalFieldRepositoryImpl( - private val context: Context, - private val moduleName: String, - private val additionalFieldLocalDataSource: IAdditionalFieldLocalDataSource, - private val geoNatureAPIClient: IGeoNatureAPIClient, -) : ISynchronizeAdditionalDataRepository { - - override suspend fun invoke(): Flow = flow { - Logger.info { "synchronize additional fields..." } - - val additionalFieldJsonReader = AdditionalFieldJsonReader() - - val additionalFields = runCatching { - geoNatureAPIClient.getAdditionalFields(moduleName.uppercase()) - .await() - .let { - additionalFieldJsonReader.read( - it.byteStream() - .bufferedReader() - .use(BufferedReader::readText) - ) - } - }.onFailure { Logger.warn { it.message } } - .getOrDefault(emptyList()) - - if (additionalFields.isEmpty()) { - emit(DataSyncStatus(state = WorkInfo.State.SUCCEEDED)) - - return@flow - } - - Logger.info { "${additionalFields.size} additional field(s) found" } - - runCatching { - additionalFieldLocalDataSource.updateAdditionalFields(*additionalFields.toTypedArray()) - }.onFailure { - emit( - DataSyncStatus( - state = WorkInfo.State.FAILED, - syncMessage = context.getString(R.string.sync_data_additional_fields_error) - ) - ) - } - - emit( - DataSyncStatus( - state = WorkInfo.State.SUCCEEDED, - syncMessage = context.getString( - R.string.sync_data_additional_fields, - additionalFields.size - ) - ) - ) - } -} \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/usecase/GetEditableFieldsUseCase.kt b/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/usecase/GetEditableFieldsUseCase.kt index befd0d51..c899b06c 100644 --- a/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/usecase/GetEditableFieldsUseCase.kt +++ b/occtax/src/main/java/fr/geonature/occtax/features/nomenclature/usecase/GetEditableFieldsUseCase.kt @@ -35,11 +35,12 @@ class GetEditableFieldsUseCase @Inject constructor( ) } } + - additionalFieldRepository.getAllAdditionalFields( + if (params.withAdditionalFields) additionalFieldRepository.getAllAdditionalFields( params.datasetId, params.type ) - .getOrDefault(emptyList())) + .getOrDefault(emptyList()) else emptyList()) + // set media type at last position .sortedWith { a, b -> if (a.viewType == EditableField.ViewType.MEDIA) 1 @@ -69,6 +70,7 @@ class GetEditableFieldsUseCase @Inject constructor( data class Params( val datasetId: Long? = null, + val withAdditionalFields: Boolean = false, val type: EditableField.Type, val settings: List = listOf(), val taxonomy: Taxonomy? = null diff --git a/occtax/src/main/java/fr/geonature/occtax/features/record/presentation/ObservationRecordViewModel.kt b/occtax/src/main/java/fr/geonature/occtax/features/record/presentation/ObservationRecordViewModel.kt index 9dd39187..cc422568 100644 --- a/occtax/src/main/java/fr/geonature/occtax/features/record/presentation/ObservationRecordViewModel.kt +++ b/occtax/src/main/java/fr/geonature/occtax/features/record/presentation/ObservationRecordViewModel.kt @@ -175,12 +175,19 @@ class ObservationRecordViewModel @Inject constructor( * Start edit the given [ObservationRecord]. * * @param observationRecord the [ObservationRecord] to edit + * @param withAdditionalFields whether we want to manage additional fields */ - fun startEdit(observationRecord: ObservationRecord) { + fun startEdit( + observationRecord: ObservationRecord, + withAdditionalFields: Boolean = false + ) { Logger.info { "loading default nomenclature values from record '${observationRecord.internalId}'..." } editObservationRecordUseCase( - EditObservationRecordUseCase.Params(observationRecord), + EditObservationRecordUseCase.Params( + observationRecord, + withAdditionalFields + ), viewModelScope ) { it.fold( diff --git a/occtax/src/main/java/fr/geonature/occtax/features/record/usecase/EditObservationRecordUseCase.kt b/occtax/src/main/java/fr/geonature/occtax/features/record/usecase/EditObservationRecordUseCase.kt index a2f1e6f4..ef592d49 100644 --- a/occtax/src/main/java/fr/geonature/occtax/features/record/usecase/EditObservationRecordUseCase.kt +++ b/occtax/src/main/java/fr/geonature/occtax/features/record/usecase/EditObservationRecordUseCase.kt @@ -23,7 +23,12 @@ class EditObservationRecordUseCase @Inject constructor( Logger.info { "loading default nomenclature values from record '${params.observationRecord.internalId}'..." } var result = - setDefaultNomenclatureValuesUseCase.run(SetDefaultNomenclatureValuesUseCase.Params(params.observationRecord)) + setDefaultNomenclatureValuesUseCase.run( + SetDefaultNomenclatureValuesUseCase.Params( + params.observationRecord, + params.withAdditionalFields + ) + ) if (result.isFailure) { Logger.error { "failed to load default nomenclature values from record '${params.observationRecord.internalId}" } @@ -48,5 +53,8 @@ class EditObservationRecordUseCase @Inject constructor( return result } - data class Params(val observationRecord: ObservationRecord) + data class Params( + val observationRecord: ObservationRecord, + val withAdditionalFields: Boolean = false + ) } \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/features/record/usecase/SetDefaultNomenclatureValuesUseCase.kt b/occtax/src/main/java/fr/geonature/occtax/features/record/usecase/SetDefaultNomenclatureValuesUseCase.kt index 5166cae6..51c0cdea 100644 --- a/occtax/src/main/java/fr/geonature/occtax/features/record/usecase/SetDefaultNomenclatureValuesUseCase.kt +++ b/occtax/src/main/java/fr/geonature/occtax/features/record/usecase/SetDefaultNomenclatureValuesUseCase.kt @@ -47,11 +47,11 @@ class SetDefaultNomenclatureValuesUseCase @Inject constructor( val editableFieldsInformation = nomenclatureRepository.getEditableFields(EditableField.Type.INFORMATION) - .getOrElse { emptyList() } + additionalFieldRepository.getAllAdditionalFields( + .getOrElse { emptyList() } + if (params.withAdditionalFields) additionalFieldRepository.getAllAdditionalFields( observationRecord.dataset.datasetId, EditableField.Type.INFORMATION ) - .getOrElse { emptyList() } + .getOrElse { emptyList() } else emptyList() if (editableFieldsInformation.isEmpty()) { Logger.warn { @@ -67,11 +67,11 @@ class SetDefaultNomenclatureValuesUseCase @Inject constructor( val editableFieldsCounting = nomenclatureRepository.getEditableFields(EditableField.Type.COUNTING) - .getOrElse { emptyList() } + additionalFieldRepository.getAllAdditionalFields( + .getOrElse { emptyList() } + if (params.withAdditionalFields) additionalFieldRepository.getAllAdditionalFields( observationRecord.dataset.datasetId, EditableField.Type.COUNTING ) - .getOrElse { emptyList() } + .getOrElse { emptyList() } else emptyList() if (editableFieldsCounting.isEmpty()) { Logger.warn { @@ -162,5 +162,8 @@ class SetDefaultNomenclatureValuesUseCase @Inject constructor( } } - data class Params(val observationRecord: ObservationRecord) + data class Params( + val observationRecord: ObservationRecord, + val withAdditionalFields: Boolean = false + ) } \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/settings/NomenclatureSettings.kt b/occtax/src/main/java/fr/geonature/occtax/settings/NomenclatureSettings.kt index b0ef06a0..163f1d60 100644 --- a/occtax/src/main/java/fr/geonature/occtax/settings/NomenclatureSettings.kt +++ b/occtax/src/main/java/fr/geonature/occtax/settings/NomenclatureSettings.kt @@ -10,7 +10,17 @@ import kotlinx.parcelize.Parcelize */ @Parcelize data class NomenclatureSettings( + /** + * Whether we want to save locally and only during a session of use selected nomenclature values + * as default values. + */ val saveDefaultValues: Boolean = false, + + /** + * Whether we want to show additional fields + */ + val withAdditionalFields: Boolean = false, + val information: List, val counting: List ) : Parcelable \ No newline at end of file diff --git a/occtax/src/main/java/fr/geonature/occtax/settings/io/OnAppSettingsJsonReaderListenerImpl.kt b/occtax/src/main/java/fr/geonature/occtax/settings/io/OnAppSettingsJsonReaderListenerImpl.kt index e71ba34b..d68bcddf 100644 --- a/occtax/src/main/java/fr/geonature/occtax/settings/io/OnAppSettingsJsonReaderListenerImpl.kt +++ b/occtax/src/main/java/fr/geonature/occtax/settings/io/OnAppSettingsJsonReaderListenerImpl.kt @@ -126,6 +126,7 @@ class OnAppSettingsJsonReaderListenerImpl : } var saveDefaultValues = false + var withAdditionalFields = false val information = mutableListOf() val counting = mutableListOf() @@ -134,6 +135,7 @@ class OnAppSettingsJsonReaderListenerImpl : while (reader.hasNext()) { when (reader.nextName()) { "save_default_values" -> saveDefaultValues = reader.nextBooleanOrElse { false } + "additional_fields" -> withAdditionalFields = reader.nextBooleanOrElse { false } "information" -> information.addAll(readPropertySettingsAsList(reader)) "counting" -> counting.addAll(readPropertySettingsAsList(reader)) else -> reader.skipValue() @@ -144,6 +146,7 @@ class OnAppSettingsJsonReaderListenerImpl : return NomenclatureSettings( saveDefaultValues = saveDefaultValues, + withAdditionalFields = withAdditionalFields, information = information, counting = counting ) @@ -206,11 +209,13 @@ class OnAppSettingsJsonReaderListenerImpl : default ) } + JsonToken.STRING -> PropertySettings( reader.nextString(), visible = true, default = true ) + else -> { reader.skipValue() null diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/home/HomeActivity.kt b/occtax/src/main/java/fr/geonature/occtax/ui/home/HomeActivity.kt index 48f6794d..0fa349f9 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/home/HomeActivity.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/home/HomeActivity.kt @@ -147,6 +147,7 @@ class HomeActivity : AppCompatActivity(), appSettings?.dataSyncSettings?.also { dataSyncSettings -> dataSyncViewModel.startSync( dataSyncSettings, + appSettings?.nomenclatureSettings?.withAdditionalFields ?: false, HomeActivity::class.java, MainApplication.CHANNEL_DATA_SYNCHRONIZATION ) @@ -201,6 +202,7 @@ class HomeActivity : AppCompatActivity(), } else { dataSyncViewModel.startSync( dataSyncSettings, + appSettings?.nomenclatureSettings?.withAdditionalFields ?: false, HomeActivity::class.java, MainApplication.CHANNEL_DATA_SYNCHRONIZATION ) @@ -402,6 +404,7 @@ class HomeActivity : AppCompatActivity(), it.dataSyncSettings?.also { dataSyncSettings -> dataSyncViewModel.configurePeriodicSync( dataSyncSettings, + appSettings?.nomenclatureSettings?.withAdditionalFields ?: false, HomeActivity::class.java, MainApplication.CHANNEL_DATA_SYNCHRONIZATION ) @@ -409,6 +412,7 @@ class HomeActivity : AppCompatActivity(), if (dataSyncViewModel.lastSynchronizedDate.value?.second == null) { dataSyncViewModel.startSync( dataSyncSettings, + appSettings?.nomenclatureSettings?.withAdditionalFields ?: false, HomeActivity::class.java, MainApplication.CHANNEL_DATA_SYNCHRONIZATION ) @@ -434,6 +438,8 @@ class HomeActivity : AppCompatActivity(), dataSyncViewModel.startSync( dataSyncSettings, + appSettings?.nomenclatureSettings?.withAdditionalFields + ?: false, HomeActivity::class.java, MainApplication.CHANNEL_DATA_SYNCHRONIZATION ) @@ -446,6 +452,8 @@ class HomeActivity : AppCompatActivity(), dataSyncViewModel.startSync( dataSyncSettings, + appSettings?.nomenclatureSettings?.withAdditionalFields + ?: false, HomeActivity::class.java, MainApplication.CHANNEL_DATA_SYNCHRONIZATION ) diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/InputPagerFragmentActivity.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/InputPagerFragmentActivity.kt index fca4c55f..c53e2081 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/InputPagerFragmentActivity.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/InputPagerFragmentActivity.kt @@ -184,11 +184,15 @@ class InputPagerFragmentActivity : AbstractPagerFragmentActivity(), R.string.pager_fragment_taxa_title to TaxaFragment.newInstance(appSettings.areaObservationDuration), R.string.pager_fragment_information_title to InformationFragment.newInstance( saveDefaultValues = appSettings.nomenclatureSettings?.saveDefaultValues ?: false, + withAdditionalFields = appSettings.nomenclatureSettings?.withAdditionalFields + ?: false, *appSettings.nomenclatureSettings?.information?.toTypedArray() ?: emptyArray() ), R.string.pager_fragment_counting_title to CountingFragment.newInstance( saveDefaultValues = appSettings.nomenclatureSettings?.saveDefaultValues ?: false, + withAdditionalFields = appSettings.nomenclatureSettings?.withAdditionalFields + ?: false, *appSettings.nomenclatureSettings?.counting?.toTypedArray() ?: emptyArray() ), diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/CountingFragment.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/CountingFragment.kt index 78787091..48c83535 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/CountingFragment.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/CountingFragment.kt @@ -220,6 +220,10 @@ class CountingFragment : AbstractInputFragment() { ARG_SAVE_DEFAULT_VALUES, false ) ?: false, + arguments?.getBoolean( + ARG_WITH_ADDITIONAL_FIELDS, + false + ) ?: false, *(arguments?.getParcelableArrayCompat(ARG_PROPERTIES) ?: emptyArray()) ) ) @@ -284,6 +288,7 @@ class CountingFragment : AbstractInputFragment() { companion object { private const val ARG_SAVE_DEFAULT_VALUES = "arg_save_default_values" + private const val ARG_WITH_ADDITIONAL_FIELDS = "arg_with_additional_fields" private const val ARG_PROPERTIES = "arg_properties" /** @@ -294,6 +299,7 @@ class CountingFragment : AbstractInputFragment() { @JvmStatic fun newInstance( saveDefaultValues: Boolean = false, + withAdditionalFields: Boolean = false, vararg propertySettings: PropertySettings ) = CountingFragment().apply { arguments = Bundle().apply { @@ -301,6 +307,10 @@ class CountingFragment : AbstractInputFragment() { ARG_SAVE_DEFAULT_VALUES, saveDefaultValues ) + putBoolean( + ARG_WITH_ADDITIONAL_FIELDS, + withAdditionalFields + ) putParcelableArray( ARG_PROPERTIES, propertySettings diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt index 8bf7c0ab..ffac0de4 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataActivity.kt @@ -70,6 +70,10 @@ class EditCountingMetadataActivity : AppCompatActivity(), EXTRA_SAVE_DEFAULT_VALUES, false ), + intent.getBooleanExtra( + EXTRA_WITH_ADDITIONAL_FIELDS, + false + ), *(intent.getParcelableArrayExtraCompat(EXTRA_PROPERTIES) ?: emptyArray()) ) @@ -141,6 +145,7 @@ class EditCountingMetadataActivity : AppCompatActivity(), const val EXTRA_TAXON_RECORD = "extra_taxon_record" const val EXTRA_COUNTING_RECORD = "extra_counting_record" const val EXTRA_SAVE_DEFAULT_VALUES = "extra_save_default_values" + const val EXTRA_WITH_ADDITIONAL_FIELDS = "extra_with_additional_fields" const val EXTRA_PROPERTIES = "extra_properties" fun newIntent( @@ -149,6 +154,7 @@ class EditCountingMetadataActivity : AppCompatActivity(), taxonRecord: TaxonRecord, countingRecord: CountingRecord? = null, saveDefaultValues: Boolean = false, + withAdditionalFields: Boolean = false, vararg propertySettings: PropertySettings ): Intent { return Intent( @@ -173,6 +179,10 @@ class EditCountingMetadataActivity : AppCompatActivity(), EXTRA_SAVE_DEFAULT_VALUES, saveDefaultValues ) + putExtra( + EXTRA_WITH_ADDITIONAL_FIELDS, + withAdditionalFields + ) putExtra( EXTRA_PROPERTIES, propertySettings diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt index 33269f1b..d56704a6 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/counting/EditCountingMetadataFragment.kt @@ -342,6 +342,10 @@ class EditCountingMetadataFragment : Fragment() { -1L ) ?.takeIf { it >= 0L }, + arguments?.getBoolean( + ARG_WITH_ADDITIONAL_FIELDS, + false + ) ?: false, EditableField.Type.COUNTING, (arguments?.getParcelableArrayCompat(ARG_PROPERTIES) ?.toList() ?: emptyList()), @@ -417,6 +421,7 @@ class EditCountingMetadataFragment : Fragment() { private const val ARG_TAXON_RECORD = "arg_taxon_record" private const val ARG_COUNTING_RECORD = "arg_counting_record" private const val ARG_SAVE_DEFAULT_VALUES = "arg_save_default_values" + private const val ARG_WITH_ADDITIONAL_FIELDS = "arg_with_additional_fields" private const val ARG_PROPERTIES = "arg_properties" private const val ADD_PHOTO_DIALOG_FRAGMENT = "add_photo_dialog_fragment" @@ -433,6 +438,7 @@ class EditCountingMetadataFragment : Fragment() { taxonRecord: TaxonRecord, countingRecord: CountingRecord? = null, saveDefaultValues: Boolean = false, + withAdditionalFields: Boolean = false, vararg propertySettings: PropertySettings ) = EditCountingMetadataFragment().apply { arguments = Bundle().apply { @@ -442,6 +448,10 @@ class EditCountingMetadataFragment : Fragment() { it ) } + putBoolean( + ARG_WITH_ADDITIONAL_FIELDS, + withAdditionalFields + ) putParcelable( ARG_TAXON_RECORD, taxonRecord diff --git a/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/InformationFragment.kt b/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/InformationFragment.kt index e1fe174a..1b46bff7 100644 --- a/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/InformationFragment.kt +++ b/occtax/src/main/java/fr/geonature/occtax/ui/input/informations/InformationFragment.kt @@ -222,6 +222,10 @@ class InformationFragment : AbstractInputFragment() { override fun refreshView() { nomenclatureViewModel.getEditableFields( observationRecord?.dataset?.datasetId, + arguments?.getBoolean( + ARG_WITH_ADDITIONAL_FIELDS, + false + ) ?: false, EditableField.Type.INFORMATION, (arguments?.getParcelableArrayCompat(ARG_PROPERTIES) ?.toList() ?: emptyList()), @@ -258,6 +262,7 @@ class InformationFragment : AbstractInputFragment() { companion object { private const val ARG_SAVE_DEFAULT_VALUES = "arg_save_default_values" + private const val ARG_WITH_ADDITIONAL_FIELDS = "arg_with_additional_fields" private const val ARG_PROPERTIES = "arg_properties" private const val KEY_SHOW_ALL_NOMENCLATURE_TYPES = "show_all_nomenclature_types" @@ -269,6 +274,7 @@ class InformationFragment : AbstractInputFragment() { @JvmStatic fun newInstance( saveDefaultValues: Boolean = false, + withAdditionalFields: Boolean = false, vararg propertySettings: PropertySettings ) = InformationFragment().apply { arguments = Bundle().apply { @@ -276,6 +282,10 @@ class InformationFragment : AbstractInputFragment() { ARG_SAVE_DEFAULT_VALUES, saveDefaultValues ) + putBoolean( + ARG_WITH_ADDITIONAL_FIELDS, + withAdditionalFields + ) putParcelableArray( ARG_PROPERTIES, propertySettings diff --git a/occtax/src/main/res/values-fr/strings.xml b/occtax/src/main/res/values-fr/strings.xml index aa0c00f4..74869d78 100644 --- a/occtax/src/main/res/values-fr/strings.xml +++ b/occtax/src/main/res/values-fr/strings.xml @@ -20,8 +20,6 @@ Dernière synchronisation\n%s le EEE dd MMM yyyy à kk:mm:ss jamais - Champs additionnels : %1$d - Champs additionnels : échec Ouvrir le menu latéral Fermer le menu latéral diff --git a/occtax/src/main/res/values/strings.xml b/occtax/src/main/res/values/strings.xml index b54ae232..e4adc314 100644 --- a/occtax/src/main/res/values/strings.xml +++ b/occtax/src/main/res/values/strings.xml @@ -23,8 +23,6 @@ Last synchronization\n%s EEE dd MMM yyyy, kk:mm:ss never - Additional fields: %1$d - Additional fields: failure Open drawer menu Close drawer menu diff --git a/occtax/src/test/java/fr/geonature/occtax/features/nomenclature/usecase/GetEditableFieldsUseCaseTest.kt b/occtax/src/test/java/fr/geonature/occtax/features/nomenclature/usecase/GetEditableFieldsUseCaseTest.kt index 8f0ab686..6061bcd2 100644 --- a/occtax/src/test/java/fr/geonature/occtax/features/nomenclature/usecase/GetEditableFieldsUseCaseTest.kt +++ b/occtax/src/test/java/fr/geonature/occtax/features/nomenclature/usecase/GetEditableFieldsUseCaseTest.kt @@ -271,6 +271,157 @@ class GetEditableFieldsUseCaseTest { ) } + @Test + fun `should get all editable fields with additional fields`() = + runTest { + // given some editable fields + coEvery { + nomenclatureRepository.getEditableFields(any()) + } returns Result.success( + listOf( + EditableField( + EditableField.Type.INFORMATION, + "METH_OBS", + EditableField.ViewType.NOMENCLATURE_TYPE, + label = "Méthodes d'observation" + ), + EditableField( + EditableField.Type.INFORMATION, + "ETA_BIO", + EditableField.ViewType.NOMENCLATURE_TYPE, + label = "Etat biologique de l'observation", + visible = false + ) + ) + ) + + // with additional fields + coEvery { + additionalFieldRepository.getAllAdditionalFields( + any(), + any() + ) + } returns Result.success( + listOf( + EditableField( + EditableField.Type.INFORMATION, + "as_text", + EditableField.ViewType.TEXT_SIMPLE, + label = "As text" + ) + ) + ) + + // and no default property values + coEvery { defaultPropertyValueRepository.getPropertyValues() } returns Right(listOf()) + + // when fetching all editable fields with default value + val result = getEditableFieldsUseCase.run( + GetEditableFieldsUseCase.Params( + withAdditionalFields = true, + type = EditableField.Type.INFORMATION + ) + ) + + // then + assertEquals( + listOf( + EditableField( + EditableField.Type.INFORMATION, + "METH_OBS", + EditableField.ViewType.NOMENCLATURE_TYPE, + label = "Méthodes d'observation" + ), + EditableField( + EditableField.Type.INFORMATION, + "ETA_BIO", + EditableField.ViewType.NOMENCLATURE_TYPE, + label = "Etat biologique de l'observation", + visible = false + ), + EditableField( + EditableField.Type.INFORMATION, + "as_text", + EditableField.ViewType.TEXT_SIMPLE, + label = "As text" + ) + ).sortedBy { it.code }, + result.getOrThrow() + .sortedBy { it.code } + ) + } + + @Test + fun `should get all editable fields with no additional fields`() = + runTest { + // given some editable fields + coEvery { + nomenclatureRepository.getEditableFields(any()) + } returns Result.success( + listOf( + EditableField( + EditableField.Type.INFORMATION, + "METH_OBS", + EditableField.ViewType.NOMENCLATURE_TYPE, + label = "Méthodes d'observation" + ), + EditableField( + EditableField.Type.INFORMATION, + "ETA_BIO", + EditableField.ViewType.NOMENCLATURE_TYPE, + label = "Etat biologique de l'observation", + visible = false + ) + ) + ) + + // with additional fields + coEvery { + additionalFieldRepository.getAllAdditionalFields( + any(), + any() + ) + } returns Result.success( + listOf( + EditableField( + EditableField.Type.INFORMATION, + "as_text", + EditableField.ViewType.TEXT_SIMPLE, + label = "As text" + ) + ) + ) + + // and no default property values + coEvery { defaultPropertyValueRepository.getPropertyValues() } returns Right(listOf()) + + // when fetching all editable fields with default value + val result = getEditableFieldsUseCase.run( + GetEditableFieldsUseCase.Params(type = EditableField.Type.INFORMATION) + ) + + // then + assertEquals( + listOf( + EditableField( + EditableField.Type.INFORMATION, + "METH_OBS", + EditableField.ViewType.NOMENCLATURE_TYPE, + label = "Méthodes d'observation" + ), + EditableField( + EditableField.Type.INFORMATION, + "ETA_BIO", + EditableField.ViewType.NOMENCLATURE_TYPE, + label = "Etat biologique de l'observation", + visible = false + ) + ).sortedBy { it.code }, + result.getOrThrow() + .sortedBy { it.code } + ) + } + @Test fun `should return NoNomenclatureTypeFoundLocallyFailure if no nomenclature types was found`() = runTest { diff --git a/occtax/src/test/java/fr/geonature/occtax/features/record/usecase/SetDefaultNomenclatureValuesUseCaseTest.kt b/occtax/src/test/java/fr/geonature/occtax/features/record/usecase/SetDefaultNomenclatureValuesUseCaseTest.kt index ac48da40..b1babc26 100644 --- a/occtax/src/test/java/fr/geonature/occtax/features/record/usecase/SetDefaultNomenclatureValuesUseCaseTest.kt +++ b/occtax/src/test/java/fr/geonature/occtax/features/record/usecase/SetDefaultNomenclatureValuesUseCaseTest.kt @@ -168,7 +168,7 @@ class SetDefaultNomenclatureValuesUseCaseTest { } @Test - fun `should return an observation records with all property values`() = + fun `should return an observation records with all property values with additional fields`() = runTest { // given some observation record val observationRecord = ObservationRecord(internalId = 1234).apply { @@ -377,7 +377,12 @@ class SetDefaultNomenclatureValuesUseCaseTest { // when loading all default nomenclature values from use case val result = - setDefaultNomenclatureValuesUseCase.run(SetDefaultNomenclatureValuesUseCase.Params(observationRecord)) + setDefaultNomenclatureValuesUseCase.run( + SetDefaultNomenclatureValuesUseCase.Params( + observationRecord, + withAdditionalFields = true + ) + ) // then assertTrue(result.isSuccess) @@ -454,6 +459,293 @@ class SetDefaultNomenclatureValuesUseCaseTest { ) } + @Test + fun `should return an observation records with all property values with no additional fields`() = + runTest { + // given some observation record + val observationRecord = ObservationRecord(internalId = 1234).apply { + taxa.add( + Taxon( + 8L, + "taxon_01", + Taxonomy( + "Animalia", + "Ascidies" + ) + ) + ) + .apply { + listOf( + PropertyValue.Number( + "ETA_BIO", + 158L + ) + ).map { it.toPair() } + .forEach { + properties[it.first] = it.second + } + counting.addOrUpdate( + counting.create() + .apply { + listOf( + PropertyValue.Nomenclature( + "STADE_VIE", + null, + 2L + ), + PropertyValue.Number( + "count_min", + 1 + ), + PropertyValue.Number( + "count_max", + 2 + ) + ).map { it.toPair() } + .forEach { + properties[it.first] = it.second + } + additionalFields = listOf( + PropertyValue.Number( + "as_TYPE_PROTOCOLE", + 387L + ), + PropertyValue.Number( + "some_attribute_as_number", + 42L + ) + ) + }) + } + } + + // and some default nomenclature values + coEvery { + nomenclatureRepository.getEditableFields(EditableField.Type.DEFAULT) + } returns Result.success( + listOf( + EditableField( + EditableField.Type.DEFAULT, + "TYP_GRP", + EditableField.ViewType.NOMENCLATURE_TYPE, + "TYP_GRP", + ).apply { + value = PropertyValue.Nomenclature( + "TYP_GRP", + "NSP", + 129L + ) + } + ) + ) + coEvery { + nomenclatureRepository.getEditableFields(EditableField.Type.INFORMATION) + } returns Result.success( + listOf( + EditableField( + EditableField.Type.INFORMATION, + "ETA_BIO", + EditableField.ViewType.NOMENCLATURE_TYPE, + "ETA_BIO", + ).apply { + value = PropertyValue.Nomenclature( + "ETA_BIO", + "NSP", + 152L + ) + } + ) + ) + coEvery { + nomenclatureRepository.getEditableFields(EditableField.Type.COUNTING) + } returns Result.success( + listOf( + EditableField( + EditableField.Type.COUNTING, + "STADE_VIE", + EditableField.ViewType.NOMENCLATURE_TYPE, + "STADE_VIE", + ).apply { + value = PropertyValue.Nomenclature( + "STADE_VIE", + "Indéterminé", + 2L + ) + } + ) + ) + coEvery { + additionalFieldRepository.getAllAdditionalFields( + any(), + EditableField.Type.INFORMATION + ) + } returns Result.success(listOf()) + coEvery { + additionalFieldRepository.getAllAdditionalFields( + any(), + EditableField.Type.COUNTING + ) + } returns Result.success( + listOf( + EditableField( + EditableField.Type.INFORMATION, + "as_TYPE_PROTOCOLE", + EditableField.ViewType.NOMENCLATURE_TYPE, + "TYPE_PROTOCOLE" + ) + ) + ) + + // and with some nomenclature values + coEvery { + nomenclatureRepository.getNomenclatureValuesByTypeAndTaxonomy( + "ETA_BIO", + any() + ) + } returns Result.success( + listOf( + Nomenclature( + 152L, + "0", + "007.000", + "NSP", + typeId = 7L + ), + Nomenclature( + id = 158L, + code = "2", + hierarchy = "007.002", + defaultLabel = "Observé vivant", + typeId = 7L + ) + ) + ) + coEvery { + nomenclatureRepository.getNomenclatureValuesByTypeAndTaxonomy( + "STADE_VIE", + any() + ) + } returns Result.success( + listOf( + Nomenclature( + 2L, + "1", + "010.001", + "Indéterminé", + typeId = 10L + ), + Nomenclature( + id = 1L, + code = "0", + hierarchy = "010.000", + defaultLabel = "Inconnu", + typeId = 10L + ) + ) + ) + coEvery { + nomenclatureRepository.getNomenclatureValuesByTypeAndTaxonomy( + "TYPE_PROTOCOLE", + any() + ) + } returns Result.success( + listOf( + Nomenclature( + id = 387L, + code = "0", + hierarchy = "112.000", + defaultLabel = "Inconnu", + typeId = 112L + ), + Nomenclature( + id = 388L, + code = "1", + hierarchy = "112.001", + defaultLabel = "Protocole de collecte", + typeId = 112L + ) + ) + ) + + // when loading all default nomenclature values from use case + val result = setDefaultNomenclatureValuesUseCase.run( + SetDefaultNomenclatureValuesUseCase.Params(observationRecord) + ) + + // then + assertTrue(result.isSuccess) + assertEquals( + ObservationRecord(internalId = 1234).apply { + listOf( + PropertyValue.Nomenclature( + "TYP_GRP", + "NSP", + 129L + ) + ).map { it.toPair() } + .forEach { + properties[it.first] = it.second + } + + taxa.add( + Taxon( + 8L, + "taxon_01", + Taxonomy( + "Animalia", + "Ascidies" + ) + ) + ) + .apply { + listOf( + PropertyValue.Nomenclature( + "ETA_BIO", + "Observé vivant", + 158L + ) + ).map { it.toPair() } + .forEach { + properties[it.first] = it.second + } + counting.addOrUpdate( + counting.create() + .apply { + listOf( + PropertyValue.Nomenclature( + "STADE_VIE", + "Indéterminé", + 2L + ), + PropertyValue.Number( + "count_min", + 1 + ), + PropertyValue.Number( + "count_max", + 2 + ) + ).map { it.toPair() } + .forEach { + properties[it.first] = it.second + } + additionalFields = listOf( + PropertyValue.Number( + "as_TYPE_PROTOCOLE", + 387L + ), + PropertyValue.Number( + "some_attribute_as_number", + 42L + ) + ) + }) + } + }, + result.getOrThrow() + ) + } + @Test fun `should return a NoDefaultNomenclatureValuesFoundException failure if failed to load default nomenclature values`() = runTest { diff --git a/occtax/src/test/java/fr/geonature/occtax/settings/io/AppSettingsJsonReaderTest.kt b/occtax/src/test/java/fr/geonature/occtax/settings/io/AppSettingsJsonReaderTest.kt index dee018f7..2a16c76f 100644 --- a/occtax/src/test/java/fr/geonature/occtax/settings/io/AppSettingsJsonReaderTest.kt +++ b/occtax/src/test/java/fr/geonature/occtax/settings/io/AppSettingsJsonReaderTest.kt @@ -80,6 +80,7 @@ class AppSettingsJsonReaderTest { inputSettings = InputSettings(dateSettings = InputDateSettings.DEFAULT), nomenclatureSettings = NomenclatureSettings( saveDefaultValues = true, + withAdditionalFields = true, information = arrayListOf( PropertySettings( "METH_OBS", diff --git a/occtax/src/test/resources/fixtures/settings_occtax.json b/occtax/src/test/resources/fixtures/settings_occtax.json index f2c0b201..f49e2f53 100644 --- a/occtax/src/test/resources/fixtures/settings_occtax.json +++ b/occtax/src/test/resources/fixtures/settings_occtax.json @@ -31,6 +31,7 @@ "nomenclature": { "additional_fields": true, "save_default_values": true, + "additional_fields": true, "information": [ "METH_OBS", {