Skip to content

Commit

Permalink
Merge pull request #301 from cph-cachet/develop
Browse files Browse the repository at this point in the history
Release 1.0.0-alpha.36
  • Loading branch information
Whathecode authored Sep 3, 2021
2 parents 3461b01 + 822982c commit 98fccd8
Show file tree
Hide file tree
Showing 91 changed files with 2,573 additions and 222 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ Two key **design goals** differentiate this project from similar projects:
- [Application services](docs/carp-deployments.md#application-services)
- [Clients](docs/carp-clients.md)
- [Study runtime state](docs/carp-clients.md#study-runtime-state)
- [Data](docs/carp-data.md)
- [Data streams](docs/carp-data.md#data-streams)
- [Application services](docs/carp-data.md#application-services)
- [Common](docs/carp-common.md)
- [Built-in types](docs/carp-common.md#built-in-types)
- [Infrastructure helpers](#infrastructure-helpers)
Expand Down Expand Up @@ -61,8 +64,11 @@ Two key **design goals** differentiate this project from similar projects:

[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dk.cachet.carp.clients/carp.clients.core/badge.svg?color=orange)](https://mvnrepository.com/artifact/dk.cachet.carp.clients) [![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/dk.cachet.carp.clients/carp.clients.core?server=https%3A%2F%2Foss.sonatype.org)](https://oss.sonatype.org/content/repositories/snapshots/dk/cachet/carp/clients/)

- [**Data**](docs/carp-data.md): Contains all pseudonymized data. In combination with the original study protocol, the full provenance of the data (when/why it was collected) is known.

[![Maven Central](https://maven-badges.herokuapp.com/maven-central/dk.cachet.carp.data/carp.data.core/badge.svg?color=orange)](https://mvnrepository.com/artifact/dk.cachet.carp.data) [![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/dk.cachet.carp.data/carp.data.core?server=https%3A%2F%2Foss.sonatype.org)](https://oss.sonatype.org/content/repositories/snapshots/dk/cachet/carp/data/)

- **Resources**: Contains a simple file store for resources (such as images, videos, and text documents) which can be referenced from within study protocols to be used during a study.
- **Data**: Contains all pseudonymized data. In combination with the original study protocol, the full provenance of the data (when/why it was collected) is known.
- **Analysis**: An analysis subsystem sits in between the data store and 'studies' subsystem, enabling common data analytics but also offering anonimity-preserving features such as k-anonymity.
- **Supporting subystems**:
- [**Common**](docs/carp-common.md): Implements helper classes and base types relied upon by all subsystems.
Expand All @@ -79,7 +85,9 @@ which subsequently get passed to the deployments and clients subsystem.

- **carp.detekt**: Includes static code analysis extensions for [detekt](https://github.com/arturbosch/detekt), used when building this project to ensure conventions are followed.

Not all subsystems are implemented yet. Currently, this project contains an unstable (not backwards compatible) alpha version of the protocols, deployments, client, and studies subsystems. Many changes will happen as the rest of the infrastructure is implemented.
Not all subsystems are implemented yet.
Currently, this project contains an unstable (not backwards compatible) alpha version of the protocols, studies, deployments, clients, and data subsystems.
Many changes will happen as the rest of the infrastructure is implemented.

## Infrastructure helpers

Expand Down Expand Up @@ -299,10 +307,10 @@ if ( status is StudyRuntimeStatus.RegisteringDevices )

In case you want to contribute, please follow our [contribution guidelines](https://github.com/cph-cachet/carp.core-kotlin/blob/develop/CONTRIBUTING.md).

We recommend using IntelliJ IDEA 2020, as this is the development environment we use and is therefore fully tested.
We recommend using IntelliJ IDEA 2021, as this is the development environment we use and is therefore fully tested.

- Open the project folder in IntelliJ 2020.
- Install the Kotlin plugin for IntelliJ IDEA (211-1.5.21-release-*): `Tools->Kotlin->Configure Kotlin Plugin Updates`
- Open the project folder in IntelliJ 2021.
- To build/test/publish, click "Edit Configurations" to add configurations for [the included Gradle tasks](#gradle-tasks), or run them from the Gradle tool window.

### Gradle tasks
Expand Down
23 changes: 6 additions & 17 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ buildscript {
ext {
// Version used for all submodule artifacts.
// Snapshot publishing changes (or adds) the suffix after '-' with 'SNAPSHOT' prior to publishing.
globalVersion = '1.0.0-alpha.35'
globalVersion = '1.0.0-alpha.36'

versions = [
// Kotlin multiplatform versions.
Expand All @@ -19,7 +19,6 @@ buildscript {

// JVM versions.
jvmTarget:'1.8',
jUnit5:'5.7.2',
dokkaPlugin:'1.5.0',
reflections:'0.9.12',

Expand Down Expand Up @@ -79,6 +78,10 @@ configure( subprojects - detektModule ) {
jvm {
compilations.main.kotlinOptions.jvmTarget = versions.jvmTarget
compilations.test.kotlinOptions.jvmTarget = versions.jvmTarget

testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
js(LEGACY) {
binaries.executable()
Expand All @@ -92,22 +95,8 @@ configure( subprojects - detektModule ) {
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmTest {
dependencies {
implementation kotlin('test')
implementation kotlin('test-junit5')
implementation "org.junit.jupiter:junit-jupiter-api:${versions.jUnit5}"
runtimeOnly "org.junit.jupiter:junit-jupiter-engine:${versions.jUnit5}"
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
}
Expand Down Expand Up @@ -281,7 +270,7 @@ configure( coreModules + commonModule ) {
sourceSets {
commonMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-datetime:${versions.datetime}"
api "org.jetbrains.kotlinx:kotlinx-datetime:${versions.datetime}"
}
}
commonTest {
Expand Down
1 change: 1 addition & 0 deletions carp.clients.core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ kotlin {
dependencies {
api project(':carp.deployments.core')
api project(':carp.protocols.core')
implementation project(':carp.data.core')
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class StudyRuntime private constructor(
{
data class DeploymentReceived(
val deploymentInformation: MasterDeviceDeployment,
val remainingDevicesToRegister: List<AnyDeviceDescriptor>
val remainingDevicesToRegister: Set<AnyDeviceDescriptor>
) : Event()

object DeploymentCompleted : Event()
Expand Down Expand Up @@ -98,7 +98,7 @@ class StudyRuntime private constructor(
createdOn = snapshot.createdOn
isDeployed = snapshot.isDeployed
deploymentInformation = snapshot.deploymentInformation
remainingDevicesToRegister = snapshot.remainingDevicesToRegister
remainingDevicesToRegister = snapshot.remainingDevicesToRegister.toSet()
isStopped = snapshot.isStopped
}
}
Expand All @@ -115,7 +115,7 @@ class StudyRuntime private constructor(
var isDeployed: Boolean = false
private set

private var remainingDevicesToRegister: List<AnyDeviceDescriptor> = emptyList()
private var remainingDevicesToRegister: Set<AnyDeviceDescriptor> = emptySet()
private var deploymentInformation: MasterDeviceDeployment? = null

/**
Expand All @@ -132,7 +132,7 @@ class StudyRuntime private constructor(
when {
deploymentInformation == null -> StudyRuntimeStatus.NotReadyForDeployment( id )
remainingDevicesToRegister.isNotEmpty() ->
StudyRuntimeStatus.RegisteringDevices( id, deploymentInformation!!, remainingDevicesToRegister.toList() )
StudyRuntimeStatus.RegisteringDevices( id, deploymentInformation!!, remainingDevicesToRegister.toSet() )
isStopped -> StudyRuntimeStatus.Stopped( id, deploymentInformation!! )
isDeployed -> StudyRuntimeStatus.Deployed( id, deploymentInformation!! )
else -> error( "Unexpected study runtime state." )
Expand Down Expand Up @@ -172,7 +172,8 @@ class StudyRuntime private constructor(
remainingDevicesToRegister = deploymentStatus.devicesStatus
.map { it.device }
.filter { it.roleName in deviceStatus.remainingDevicesToRegisterBeforeDeployment }
event( Event.DeploymentReceived( deployment, remainingDevicesToRegister.toList() ) )
.toSet()
event( Event.DeploymentReceived( deployment, remainingDevicesToRegister.toSet() ) )

// Early out in case devices need to be registered before being able to complete deployment.
if ( remainingDevicesToRegister.isNotEmpty() ) return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ data class StudyRuntimeSnapshot(
val device: AnyMasterDeviceDescriptor,
val isDeployed: Boolean,
val deploymentInformation: MasterDeviceDeployment?,
val remainingDevicesToRegister: List<AnyDeviceDescriptor>,
val remainingDevicesToRegister: Set<AnyDeviceDescriptor>,
val isStopped: Boolean
) : Snapshot<StudyRuntime>
{
Expand All @@ -32,8 +32,8 @@ data class StudyRuntimeSnapshot(
studyRuntime.device,
studyRuntime.isDeployed,
(status as? StudyRuntimeStatus.DeploymentReceived)?.deploymentInformation,
(status as? StudyRuntimeStatus.RegisteringDevices)?.remainingDevicesToRegister
?: emptyList(),
(status as? StudyRuntimeStatus.RegisteringDevices)?.remainingDevicesToRegister?.toSet()
?: emptySet(),
studyRuntime.isStopped
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ sealed class StudyRuntimeStatus
data class RegisteringDevices internal constructor(
override val id: StudyRuntimeId,
override val deploymentInformation: MasterDeviceDeployment,
val remainingDevicesToRegister: List<AnyDeviceDescriptor>
val remainingDevicesToRegister: Set<AnyDeviceDescriptor>
) : StudyRuntimeStatus(), DeploymentReceived
{
override val devicesRegistrationStatus = getDevicesRegistrationStatus( deploymentInformation )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import dk.cachet.carp.common.application.users.AccountIdentity
import dk.cachet.carp.common.domain.users.Account
import dk.cachet.carp.common.infrastructure.services.SingleThreadedEventBus
import dk.cachet.carp.common.infrastructure.test.StubDeviceDescriptor
import dk.cachet.carp.data.infrastructure.InMemoryDataStreamService
import dk.cachet.carp.deployments.application.DeploymentService
import dk.cachet.carp.deployments.application.DeploymentServiceHost
import dk.cachet.carp.deployments.application.ParticipationService
Expand Down Expand Up @@ -76,6 +77,7 @@ class ClientCodeSamples

val deploymentService = DeploymentServiceHost(
InMemoryDeploymentRepository(),
InMemoryDataStreamService(),
eventBus.createApplicationServiceAdapter( DeploymentService::class )
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import dk.cachet.carp.common.application.UUID
import dk.cachet.carp.common.application.devices.SmartphoneDeviceRegistration
import dk.cachet.carp.common.application.services.createApplicationServiceAdapter
import dk.cachet.carp.common.infrastructure.services.SingleThreadedEventBus
import dk.cachet.carp.data.infrastructure.InMemoryDataStreamService
import dk.cachet.carp.deployments.application.DeploymentService
import dk.cachet.carp.deployments.application.DeploymentServiceHost
import dk.cachet.carp.deployments.infrastructure.InMemoryDeploymentRepository
Expand All @@ -25,6 +26,7 @@ interface ClientRepositoryTest

val deploymentService = DeploymentServiceHost(
InMemoryDeploymentRepository(),
InMemoryDataStreamService(),
eventBus.createApplicationServiceAdapter( DeploymentService::class ) )
return Triple( createRepository(), deploymentService, createDataListener() )
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import dk.cachet.carp.common.application.services.createApplicationServiceAdapte
import dk.cachet.carp.common.application.users.AccountIdentity
import dk.cachet.carp.common.infrastructure.test.StubDeviceDescriptor
import dk.cachet.carp.common.infrastructure.services.SingleThreadedEventBus
import dk.cachet.carp.data.infrastructure.InMemoryDataStreamService
import dk.cachet.carp.deployments.application.DeploymentService
import dk.cachet.carp.deployments.application.DeploymentServiceHost
import dk.cachet.carp.deployments.application.StudyDeploymentStatus
Expand Down Expand Up @@ -70,6 +71,7 @@ suspend fun createStudyDeployment( protocol: StudyProtocol ): Pair<DeploymentSer

val deploymentService = DeploymentServiceHost(
InMemoryDeploymentRepository(),
InMemoryDataStreamService(),
eventBus.createApplicationServiceAdapter( DeploymentService::class ) )
val invitation = createParticipantInvitation( protocol )
val status = deploymentService.createStudyDeployment( UUID.randomUUID(), protocol.getSnapshot(), listOf( invitation ) )
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dk.cachet.carp.common.application


/**
* A helper class to construct iterable objects which hold [V] member definitions indexed on [K].
* This is similar to an enum, but removes the need for an intermediate enum type and generic type parameters are retained per member.
*
* Extend from this class as an object and assign members as follows: `val MEMBER = add( SomeMember() )`.
*/
open class EnumObjectMap<K, V> private constructor(
private val map: MutableMap<K, V>,
val keyOf: (V) -> K
) : Map<K, V> by map
{
constructor(
/**
* Specifies how to retrieve the key for the specified element.
*/
keyOf: (V) -> K
) : this( mutableMapOf(), keyOf )

/**
* Add an element using the key which is extracted from [item] using [keyOf].
*
* @throws IllegalArgumentException in case the extracted from [item] is already present in this map.
*/
protected fun <TAdd : V> add( item: TAdd ): TAdd = item.also {
val key = keyOf( it )
require( !map.contains( key ) ) { "An item with the same key is already present." }

map[ key ] = it
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dk.cachet.carp.common.application


/**
* Returns the intersection of this range with the specified [range], or an empty range if there is no intersection.
*/
fun CharRange.intersect( range: CharRange ): CharRange =
intersectRange( range ).let { CharRange( it.start, it.endInclusive ) }

/**
* Returns the intersection of this range with the specified [range], or an empty range if there is no intersection.
*/
fun IntRange.intersect( range: IntRange ): IntRange =
intersectRange( range ).let { IntRange( it.start, it.endInclusive ) }

/**
* Returns the intersection of this range with the specified [range], or an empty range if there is no intersection.
*/
fun LongRange.intersect( range: LongRange ): LongRange =
intersectRange( range ).let { LongRange( it.start, it.endInclusive ) }


/**
* Returns the intersection of this range with the specified [range], or an empty range if there is no intersection.
*/
private fun <T : Comparable<T>> ClosedRange<T>.intersectRange( range: ClosedRange<T> ): ClosedRange<T>
{
val startRange = maxOf( start, range.start )
val endRange = minOf( endInclusive, range.endInclusive )

return startRange..endRange
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package dk.cachet.carp.common.application.data
/**
* All CARP data types.
*/
object CarpDataTypes : DataTypeMetaDataList()
object CarpDataTypes : DataTypeMetaDataMap()
{
/**
* The [DataType] namespace of all CARP data type definitions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,11 @@ enum class DataTimeType
/**
* Data can be either related to one specific point in time ([POINT]), or a [TIME_SPAN].
*/
EITHER
EITHER;


/**
* Determines whether this [DataTimeType] matches the [required] [DataTimeType].
*/
fun matches( required: DataTimeType ): Boolean = required == EITHER || this == required
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package dk.cachet.carp.common.application.data

import dk.cachet.carp.common.application.EnumObjectList
import dk.cachet.carp.common.application.EnumObjectMap


/**
* A helper class to construct iterable objects which hold [DataTypeMetaData] member definitions.
* This is similar to an enum, but removes the need for an intermediate enum type and generic type parameters are retained per member.
*
* Extend from this class as an object and assign members as follows: `val SOME_TYPE = add( "dk.cachet.carp.sometype" )`.
* Extend from this class as an object and assign members as follows:
* `val SOME_TYPE = add( "dk.cachet.carp.sometype", "Some type", DataTimeType.EITHER )`.
*/
open class DataTypeMetaDataList : EnumObjectList<DataTypeMetaData>()
open class DataTypeMetaDataMap : EnumObjectMap<DataType, DataTypeMetaData>( { metaData -> metaData.type } )
{
/**
* Add a [DataTypeMetaData] for the [DataType] with [fullyQualifiedName]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import dk.cachet.carp.common.application.Trilean
import dk.cachet.carp.common.application.UUID
import dk.cachet.carp.common.application.data.CarpDataTypes
import dk.cachet.carp.common.application.data.DataType
import dk.cachet.carp.common.application.sampling.DataTypeSamplingSchemeList
import dk.cachet.carp.common.application.sampling.DataTypeSamplingSchemeMap
import dk.cachet.carp.common.application.sampling.NoOptionsSamplingScheme
import dk.cachet.carp.common.application.sampling.SamplingConfiguration
import dk.cachet.carp.common.application.tasks.TaskDescriptorList
Expand All @@ -19,7 +19,7 @@ import kotlin.reflect.KClass
@Serializable
data class AltBeacon( override val roleName: String ) : DeviceDescriptor<AltBeaconDeviceRegistration, AltBeaconDeviceRegistrationBuilder>()
{
object Sensors : DataTypeSamplingSchemeList()
object Sensors : DataTypeSamplingSchemeMap()
{
/**
* The signal strength as measured by the device listening to the [AltBeacon].
Expand All @@ -29,7 +29,7 @@ data class AltBeacon( override val roleName: String ) : DeviceDescriptor<AltBeac

object Tasks : TaskDescriptorList()

override fun getSupportedDataTypes(): Set<DataType> = Sensors.getDataTypes()
override fun getSupportedDataTypes(): Set<DataType> = Sensors.keys

override val defaultSamplingConfiguration: Map<DataType, SamplingConfiguration> = emptyMap()

Expand Down
Loading

0 comments on commit 98fccd8

Please sign in to comment.