Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions carp_core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.9.4

* fix of issue [#506](https://github.com/cph-cachet/carp.sensing-flutter/issues/506)
* added unit tests for input data types

## 1.9.3

* fix of issue [#491](https://github.com/cph-cachet/carp.sensing-flutter/issues/491)
Expand Down
1 change: 1 addition & 0 deletions carp_core/lib/carp_core.json.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ void _registerFromJsonFunctions() {
SexInput(value: Sex.Female),
FullNameInput(),
AddressInput(),
PhoneNumberInput(countryCode: '', number: ''),
SocialSecurityNumberInput(socialSecurityNumber: '', country: ''),
InformedConsentInput(name: '', userId: '', consent: '', signatureImage: ''),
DiagnosisInput(icd11Code: ''),
Expand Down
2 changes: 1 addition & 1 deletion carp_core/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: carp_core
description: The core domain model for the Copenhagen Research Platform (CARP) in Dart.
version: 1.9.3
version: 1.9.4
homepage: https://github.com/cph-cachet/carp.sensing-flutter

environment:
Expand Down
84 changes: 84 additions & 0 deletions carp_core/test/carp_core_dart_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,88 @@ void main() {
expect(task.getUrl('12345-1234', 'ecec573e-442b-4563-8e2c-62b7693011df', 1),
'https://cans.cachet.dk/portal/playground/studies/ecec573e-442b-4563-8e2c-62b7693011df/settings?participant=12345-1234&trigger_id=1');
});

group('InputData - deep assert', () {
test('- CustomInput', () async {
final dataJson = toJsonString(CustomInput(value: {'key': 'value'}));

final dataFromJson =
CustomInput.fromJson(json.decode(dataJson) as Map<String, dynamic>);
print(toJsonString(dataFromJson));
expect(toJsonString(dataFromJson), equals(dataJson));
});

test('- SexInput', () async {
final dataJson = toJsonString(SexInput(value: Sex.Male));

final dataFromJson =
SexInput.fromJson(json.decode(dataJson) as Map<String, dynamic>);
print(toJsonString(dataFromJson));
expect(toJsonString(dataFromJson), equals(dataJson));
});

test('- PhoneNumberInput', () async {
final dataJson = toJsonString(
PhoneNumberInput(countryCode: '+45', number: '12345678'));

final dataFromJson = PhoneNumberInput.fromJson(
json.decode(dataJson) as Map<String, dynamic>);
print(toJsonString(dataFromJson));
expect(toJsonString(dataFromJson), equals(dataJson));
});

test('- SocialSecurityNumberInput', () async {
final dataJson = toJsonString(SocialSecurityNumberInput(
country: '45', socialSecurityNumber: '123456-7890'));

final dataFromJson = SocialSecurityNumberInput.fromJson(
json.decode(dataJson) as Map<String, dynamic>);
print(toJsonString(dataFromJson));
expect(toJsonString(dataFromJson), equals(dataJson));
});

test('- FullNameInput', () async {
final dataJson = toJsonString(
FullNameInput(firstName: 'John', middleName: 'A.', lastName: 'Doe'));

final dataFromJson =
FullNameInput.fromJson(json.decode(dataJson) as Map<String, dynamic>);
print(toJsonString(dataFromJson));
expect(toJsonString(dataFromJson), equals(dataJson));
});

test('- AddressInput', () async {
final dataJson =
toJsonString(AddressInput(street: 'Main St', city: 'Anytown'));

final dataFromJson =
AddressInput.fromJson(json.decode(dataJson) as Map<String, dynamic>);
print(toJsonString(dataFromJson));
expect(toJsonString(dataFromJson), equals(dataJson));
});

test('- DiagnosisInput', () async {
final dataJson =
toJsonString(DiagnosisInput(diagnosis: 'Flu', icd11Code: '123456'));

final dataFromJson = DiagnosisInput.fromJson(
json.decode(dataJson) as Map<String, dynamic>);
print(toJsonString(dataFromJson));
expect(toJsonString(dataFromJson), equals(dataJson));
});

test('- InformedConsentInput', () async {
final dataJson = toJsonString(InformedConsentInput(
userId: '12345',
name: 'John Doe',
consent: 'true',
signatureImage: 'blob',
));

final dataFromJson = InformedConsentInput.fromJson(
json.decode(dataJson) as Map<String, dynamic>);
print(toJsonString(dataFromJson));
expect(toJsonString(dataFromJson), equals(dataJson));
});
});
}
72 changes: 33 additions & 39 deletions packages/carp_connectivity_package/lib/connectivity_probes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ class ConnectivityProbe extends StreamProbe {
@override
Future<bool> onStart() async {
// collect the current connectivity status on sampling start
var connectivityStatus =
await connectivity.Connectivity().checkConnectivity();
addMeasurement(Measurement.fromData(
Connectivity.fromConnectivityResult(connectivityStatus)));
var connectivityStatus = await connectivity.Connectivity().checkConnectivity();
addMeasurement(Measurement.fromData(Connectivity.fromConnectivityResult(connectivityStatus)));

return super.onStart();
}

@override
Stream<Measurement> get stream =>
connectivity.Connectivity().onConnectivityChanged.map((event) =>
Measurement.fromData(Connectivity.fromConnectivityResult(event)));
Stream<Measurement> get stream => connectivity.Connectivity()
.onConnectivityChanged
.map((event) => Measurement.fromData(Connectivity.fromConnectivityResult(event)));
}

// This probe requests access to location permissions (both on Android and iOS).
Expand Down Expand Up @@ -69,24 +67,17 @@ class BluetoothProbe extends BufferingPeriodicStreamProbe {
Stream<dynamic> get bufferingStream => FlutterBluePlus.scanResults;

@override
Future<Measurement?> getMeasurement() async =>
_data != null ? Measurement.fromData(_data!) : null;
Future<Measurement?> getMeasurement() async => _data != null ? Measurement.fromData(_data!) : null;

// if a BT-specific sampling configuration is used, we need to
// extract the services and remoteIds from it so FlutterBluePlus can
// perform filtered scanning
List<Guid> get services => (samplingConfiguration
is BluetoothScanPeriodicSamplingConfiguration)
? (samplingConfiguration as BluetoothScanPeriodicSamplingConfiguration)
.withServices
.map((e) => Guid(e))
.toList()
List<Guid> get services => (samplingConfiguration is BluetoothScanPeriodicSamplingConfiguration)
? (samplingConfiguration as BluetoothScanPeriodicSamplingConfiguration).withServices.map((e) => Guid(e)).toList()
: [];

List<String> get remoteIds => (samplingConfiguration
is BluetoothScanPeriodicSamplingConfiguration)
? (samplingConfiguration as BluetoothScanPeriodicSamplingConfiguration)
.withRemoteIds
List<String> get remoteIds => (samplingConfiguration is BluetoothScanPeriodicSamplingConfiguration)
? (samplingConfiguration as BluetoothScanPeriodicSamplingConfiguration).withRemoteIds
: [];

@override
Expand All @@ -97,8 +88,7 @@ class BluetoothProbe extends BufferingPeriodicStreamProbe {
FlutterBluePlus.startScan(
withServices: services,
withRemoteIds: remoteIds,
timeout: samplingConfiguration?.duration ??
const Duration(milliseconds: DEFAULT_TIMEOUT),
timeout: samplingConfiguration?.duration ?? const Duration(milliseconds: DEFAULT_TIMEOUT),
);
} catch (error) {
FlutterBluePlus.stopScan();
Expand Down Expand Up @@ -132,19 +122,15 @@ class BeaconProbe extends StreamProbe {
super.samplingConfiguration as BeaconRangingPeriodicSamplingConfiguration;

List<Region> get beaconRegions =>
samplingConfiguration?.beaconRegions
.map((region) => region.toRegion())
.toList() ??
[];
samplingConfiguration?.beaconRegions.map((region) => region.toRegion()).toList() ?? [];

int get beaconDistance => samplingConfiguration?.beaconDistance ?? 2;

@override
bool onInitialize() {
super.onInitialize();
if (beaconRegions.isEmpty) {
warning(
'$runtimeType - No beacon regions specified for monitoring. Will not start monitoring.');
warning('$runtimeType - No beacon regions specified for monitoring. Will not start monitoring.');
return false;
}

Expand All @@ -165,15 +151,17 @@ class BeaconProbe extends StreamProbe {
}

@override
Stream<Measurement> get stream => flutterBeacon.ranging(beaconRegions).map(
(RangingResult result) {
final closeBeacons = result.beacons
.where((beacon) => beacon.accuracy <= beaconDistance);

// If no beacons are close, still return an measurement with an empty
// list of iBeacons for this region.
return Measurement.fromData(
BeaconData(region: result.region.identifier)
Stream<Measurement> get stream async* {
await for (final monitoringResult in flutterBeacon.monitoring(beaconRegions)) {
if (monitoringResult.monitoringState == MonitoringState.inside) {
info('$runtimeType - Entered region: ${monitoringResult.region.identifier}');

yield* flutterBeacon.ranging(beaconRegions).map(
(rangingResult) {
final closeBeacons = rangingResult.beacons.where((beacon) => beacon.accuracy <= beaconDistance);

return Measurement.fromData(
BeaconData(region: rangingResult.region.identifier)
..scanResult = closeBeacons
.map((beacon) => BeaconDevice(
uuid: beacon.proximityUUID,
Expand All @@ -183,7 +171,13 @@ class BeaconProbe extends StreamProbe {
accuracy: beacon.accuracy,
proximity: beacon.proximity,
))
.toList());
},
);
.toList(),
);
},
);
} else if (monitoringResult.monitoringState == MonitoringState.outside) {
info('$runtimeType - Exited region: ${monitoringResult.region.identifier}');
}
}
}
}