Skip to content

Commit

Permalink
Merge pull request coronavirus-diary#8 from joshua-s/feature/assessments
Browse files Browse the repository at this point in the history
Assessments
  • Loading branch information
ferndot authored Mar 21, 2020
2 parents 6e5567c + a176a03 commit 08b4f3f
Show file tree
Hide file tree
Showing 23 changed files with 628 additions and 127 deletions.
37 changes: 22 additions & 15 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
PODS:
- Flutter (1.0.0)
- FMDB/SQLCipher (2.7.5):
- SQLCipher
- moor_ffi (0.0.1):
- geolocator (5.3.0):
- Flutter
- google_api_availability (2.0.3):
- Flutter
- location_permissions (2.0.5):
- Flutter
- path_provider (0.0.1):
- Flutter
- path_provider_macos (0.0.1):
- Flutter
- sqflite (0.0.1):
- share (0.5.2):
- Flutter
- FMDB/SQLCipher (~> 2.7.5)
- SQLCipher (4.1.0):
- SQLCipher/standard (= 4.1.0)
- SQLCipher/common (4.1.0)
Expand All @@ -19,36 +20,42 @@ PODS:

DEPENDENCIES:
- Flutter (from `Flutter`)
- moor_ffi (from `.symlinks/plugins/moor_ffi/ios`)
- geolocator (from `.symlinks/plugins/geolocator/ios`)
- google_api_availability (from `.symlinks/plugins/google_api_availability/ios`)
- location_permissions (from `.symlinks/plugins/location_permissions/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- share (from `.symlinks/plugins/share/ios`)
- SQLCipher (~> 4.1.0)

SPEC REPOS:
trunk:
- FMDB
- SQLCipher

EXTERNAL SOURCES:
Flutter:
:path: Flutter
moor_ffi:
:path: ".symlinks/plugins/moor_ffi/ios"
geolocator:
:path: ".symlinks/plugins/geolocator/ios"
google_api_availability:
:path: ".symlinks/plugins/google_api_availability/ios"
location_permissions:
:path: ".symlinks/plugins/location_permissions/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
path_provider_macos:
:path: ".symlinks/plugins/path_provider_macos/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
share:
:path: ".symlinks/plugins/share/ios"

SPEC CHECKSUMS:
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
moor_ffi: d66c9470c18e9cb333423bbcb493c105c6c774c6
geolocator: 7cdcf71180b80913b3cd84ab715d3b5365b378af
google_api_availability: 526574c9a5a0ae541e18c65f98e47afc11f53c8b
location_permissions: 4a49d4e5bec5b653643e551ab77963cc99bb0e4a
path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d
path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
sqflite: dc1dd7fd0c3603c33bd1c0b2e90d38182e3ddb37
share: bae0a282aab4483288913fc4dc0b935d4b491f2e
SQLCipher: efbdb52cdbe340bcd892b1b14297df4e07241b7f

PODFILE CHECKSUM: c1f56ec578e7dc933512aa9b9b86b80eb2a5b47d
Expand Down
20 changes: 14 additions & 6 deletions lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:coronavirus_diary/src/blocs/preferences/preferences.dart';
import 'package:coronavirus_diary/src/blocs/checkup/checkup.dart';
import 'package:coronavirus_diary/src/blocs/questions/questions.dart';
import 'package:coronavirus_diary/src/data/repositories/checkups.dart';
import 'package:coronavirus_diary/src/data/repositories/questions.dart';
import 'package:coronavirus_diary/src/ui/assets/theme.dart';
import 'package:coronavirus_diary/src/ui/router.dart';
Expand All @@ -21,17 +23,23 @@ class DiaryApp extends StatelessWidget {
create: (context) {
return QuestionsBloc(
questionsRepository: QuestionsRepository(),
);
)..add(LoadQuestions());
},
),
],
child: BlocBuilder<PreferencesBloc, PreferencesState>(
builder: (context, state) {
return MaterialApp(
title: 'Coronavirus Diary',
theme: appTheme,
routes: appRoutes,
initialRoute: initialRoute,
return BlocProvider<CheckupBloc>(
create: (context) => CheckupBloc(
preferencesState: state,
checkupsRepository: CheckupsRepository(),
),
child: MaterialApp(
title: 'Coronavirus Diary',
theme: appTheme,
routes: appRoutes,
initialRoute: initialRoute,
),
);
},
),
Expand Down
59 changes: 48 additions & 11 deletions lib/src/blocs/checkup/checkup_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:coronavirus_diary/src/blocs/preferences/preferences.dart';
import 'package:coronavirus_diary/src/data/models/checkups.dart';
import 'package:coronavirus_diary/src/data/repositories/checkups.dart';
import 'checkup.dart';

export 'package:coronavirus_diary/src/data/models/assessments.dart';
export 'package:coronavirus_diary/src/data/models/checkups.dart';

class CheckupBloc extends Bloc<CheckupEvent, CheckupState> {
final PreferencesState preferencesState;
final CheckupsRepository checkupsRepository;

CheckupBloc({this.preferencesState});
CheckupBloc({
this.preferencesState,
this.checkupsRepository,
});

@override
CheckupState get initialState => CheckupStateNotCreated();
Expand All @@ -35,17 +41,21 @@ class CheckupBloc extends Bloc<CheckupEvent, CheckupState> {
}

Stream<CheckupState> _mapStartCheckupToState(StartCheckup event) async* {
// Create checkup using API
yield CheckupStateCreating();
yield CheckupStateInProgress(
checkup: Checkup(
userId: preferencesState.preferences.userId,
),
);

// Create checkup using API
final Checkup newCheckup = await checkupsRepository.createCheckup(Checkup(
userId: preferencesState.preferences.userId,
));

yield CheckupStateInProgress(checkup: newCheckup);
}

Stream<CheckupState> _mapUpdateLocalCheckupToState(
UpdateLocalCheckup event) async* {
// Exit if checkup is not in progress
if (state is! CheckupStateInProgress) return;

// Don't send to API yet
print(event.updatedCheckup);
yield CheckupStateInProgress(
Expand All @@ -55,13 +65,40 @@ class CheckupBloc extends Bloc<CheckupEvent, CheckupState> {

Stream<CheckupState> _mapUpdateRemoteCheckupToState(
UpdateRemoteCheckup event) async* {
// Patch checkup using API
print('Saving checkup to server');
// Exit if checkup is not in progress
if (state is! CheckupStateInProgress) return;

// Retrieve current checkup
final CheckupStateInProgress currentState = state;
final Checkup currentCheckup = currentState.checkup;

// Patch checkup using API and return it (to handle server-side updates)
final Checkup checkup =
await checkupsRepository.updateCheckup(currentCheckup);
yield CheckupStateInProgress(checkup: checkup);
}

Stream<CheckupState> _mapCompleteCheckupToState(
CompleteCheckup event) async* {
// Patch checkup using API
yield CheckupStateCompleted();
// Exit if checkup is not in progress
if (state is! CheckupStateInProgress) return;

// Notify app that we are waiting to submit data
yield CheckupStateCompleting();

// Retrieve current checkup
final CheckupStateInProgress currentState = state;
final Checkup currentCheckup = currentState.checkup;

// Make sure checkup is up to date on server
final Checkup checkup =
await checkupsRepository.updateCheckup(currentCheckup);

// Complete checkup
final Assessment assessment =
await checkupsRepository.completeCheckup(checkup.id);

// Complete checkup using API
yield CheckupStateCompleted(assessment: assessment);
}
}
12 changes: 11 additions & 1 deletion lib/src/blocs/checkup/checkup_state.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:coronavirus_diary/src/data/models/assessments.dart';
import 'package:coronavirus_diary/src/data/models/checkups.dart';

abstract class CheckupState {
Expand All @@ -17,4 +18,13 @@ class CheckupStateInProgress extends CheckupState {
String toString() => 'CheckupStateInProgress { checkup: $checkup }';
}

class CheckupStateCompleted extends CheckupState {}
class CheckupStateCompleting extends CheckupState {}

class CheckupStateCompleted extends CheckupState {
final Assessment assessment;

const CheckupStateCompleted({this.assessment});

@override
String toString() => 'CheckupStateCompleted { assessment: $assessment }';
}
3 changes: 3 additions & 0 deletions lib/src/blocs/preferences/preferences_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ class PreferencesState {
final Preferences preferences;

const PreferencesState({this.preferences});

@override
String toString() => 'PreferencesState { preferences: $preferences }';
}
22 changes: 22 additions & 0 deletions lib/src/data/models/assessments.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:json_annotation/json_annotation.dart';

part 'assessments.g.dart';

@JsonSerializable()
class Assessment {
DateTime processed;
bool matchesPuiSymptoms;

Assessment({
this.processed,
this.matchesPuiSymptoms,
});

factory Assessment.fromJson(Map<String, dynamic> json) =>
_$AssessmentFromJson(json);
Map<String, dynamic> toJson() => _$AssessmentToJson(this);

@override
String toString() =>
'Assessment { processed: $processed, matchesPuiSymptoms: $matchesPuiSymptoms }';
}
22 changes: 22 additions & 0 deletions lib/src/data/models/assessments.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/src/data/models/checkups.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Checkup {

@override
String toString() =>
'Checkup { id: $id, created: $created, dataContributionPreference: $dataContributionPreference, '
'Checkup { id: $id, userId: $userId, created: $created, dataContributionPreference: $dataContributionPreference, '
'location: $location, subjectiveResponses: $subjectiveResponses, vitalsResponses: $vitalsResponses }';
}

Expand Down
20 changes: 16 additions & 4 deletions lib/src/data/models/preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,37 @@ import 'package:json_annotation/json_annotation.dart';
import 'package:uuid/uuid.dart';
import 'package:uuid/uuid_util.dart';

import 'assessments.dart';

part 'preferences.g.dart';

@JsonSerializable()
class Preferences {
final String userId;
final Assessment lastAssessment;

Preferences({
userId,
this.lastAssessment,
}) : userId = userId ??
Uuid().v4(options: {
'grng': UuidUtil.cryptoRNG,
});

Preferences.clone(Preferences preferences)
: this(
userId: preferences.userId,
);
Preferences cloneWith({
Assessment lastAssessment,
}) {
return Preferences(
userId: this.userId,
lastAssessment: lastAssessment ?? this.lastAssessment,
);
}

factory Preferences.fromJson(Map<String, dynamic> json) =>
_$PreferencesFromJson(json);
Map<String, dynamic> toJson() => _$PreferencesToJson(this);

@override
String toString() =>
'Preferences { userId: $userId, lastAssessment: $lastAssessment }';
}
4 changes: 4 additions & 0 deletions lib/src/data/models/preferences.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions lib/src/data/repositories/checkups.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'dart:async';
import 'dart:math';

import 'package:coronavirus_diary/src/data/models/assessments.dart';
import 'package:coronavirus_diary/src/data/models/checkups.dart';

class CheckupsRepository {
Future<Checkup> createCheckup(Checkup checkup) async {
await Future.delayed(Duration(seconds: 1));
return checkup;
}

Future<Checkup> updateCheckup(Checkup updatedCheckup) async {
await Future.delayed(Duration(seconds: 1));
return updatedCheckup;
}

Future<Assessment> completeCheckup(String id) async {
await Future.delayed(Duration(seconds: 2));
return Assessment(
processed: DateTime.now(),
matchesPuiSymptoms: Random().nextBool(),
);
}
}
3 changes: 3 additions & 0 deletions lib/src/ui/router.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import 'screens/assessment/assessment.dart';
import 'screens/checkup/checkup.dart';
import 'screens/home/home.dart';

export 'screens/assessment/assessment.dart';
export 'screens/checkup/checkup.dart';
export 'screens/home/home.dart';

var appRoutes = {
HomeScreen.routeName: (context) => HomeScreen(),
CheckupScreen.routeName: (context) => CheckupScreen(),
AssessmentScreen.routeName: (context) => AssessmentScreen(),
};

const initialRoute = HomeScreen.routeName;
Loading

0 comments on commit 08b4f3f

Please sign in to comment.