Skip to content

Commit

Permalink
feat(#660): refactor lab class
Browse files Browse the repository at this point in the history
  • Loading branch information
tamslo committed Nov 8, 2024
1 parent cfef67d commit 5966e70
Show file tree
Hide file tree
Showing 14 changed files with 137 additions and 147 deletions.
2 changes: 1 addition & 1 deletion app/generate_screendocs/sequence_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'package:provider/provider.dart';
Future<void> loadApp(WidgetTester tester) async {
// Part before runApp in lib/main.dart
await initServices();
await updateGenotypeResults();
await maybeUpdateGenotypeResults();
// Load the app
await tester.pumpWidget(
ChangeNotifierProvider(
Expand Down
1 change: 0 additions & 1 deletion app/integration_test/login_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:app/common/module.dart';
import 'package:app/login/models/lab.dart';
import 'package:app/login/module.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down
4 changes: 0 additions & 4 deletions app/lib/common/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ import 'package:url_launcher/url_launcher.dart';

Uri anniUrl([String slug = '']) =>
Uri.http('vm-slosarek01.dhclab.i.hpi.de:8000', 'api/v1/$slug');
Uri labServerUrl([String slug = '']) =>
Uri.http('vm-slosarek01.dhclab.i.hpi.de:8081', 'api/v1/$slug');
Uri keycloakUrl([String slug = '']) =>
Uri.http('vm-slosarek01.dhclab.i.hpi.de:28080', slug);

final geneticInformationUrl = Uri.https(
'medlineplus.gov',
Expand Down
9 changes: 0 additions & 9 deletions app/lib/common/models/userdata/lab_result.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import 'dart:convert';

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart';
import 'genotype.dart';

part 'lab_result.g.dart';
Expand Down Expand Up @@ -35,9 +32,3 @@ class LabResult implements Genotype {
@HiveField(3)
String allelesTested;
}

// assumes http reponse from lab server
List<LabResult> labDataFromHTTPResponse(Response resp) {
final json = jsonDecode(resp.body)['diplotypes'] as List<dynamic>;
return json.map<LabResult>(LabResult.fromJson).toList();
}
14 changes: 0 additions & 14 deletions app/lib/common/models/userdata/userdata.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import 'dart:convert';

import 'package:hive/hive.dart';
import 'package:http/http.dart';

import '../../module.dart';
import '../../utilities/hive_utils.dart';

Expand Down Expand Up @@ -136,13 +132,3 @@ Future<void> initUserData() async {
final userData = Hive.box<UserData>(_boxName);
UserData._instance = userData.get('data') ?? UserData();
}

// assumes http response from lab server
List<String> activeDrugsFromHTTPResponse(Response resp) {
var activeDrugs = <String>[];
final json = jsonDecode(resp.body) as Map<String, dynamic>;
if (json.containsKey('medications')) {
activeDrugs = List<String>.from(json['medications']);
}
return activeDrugs;
}
4 changes: 2 additions & 2 deletions app/lib/common/utilities/drug_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:http/http.dart';
import '../../app.dart';
import '../module.dart';

Future<void> updateDrugsWithGuidelines() async {
Future<void> maybeUpdateDrugsWithGuidelines() async {
final isOnline = await hasConnectionTo(anniUrl().host);
if (!isOnline && DrugsWithGuidelines.instance.version == null) {
throw Exception();
Expand All @@ -22,7 +22,7 @@ Future<void> updateDrugsWithGuidelines() async {
DrugsWithGuidelines.instance.drugs = data.drugs;
DrugsWithGuidelines.instance.version = data.version;
await DrugsWithGuidelines.save();
await updateGenotypeResults();
await maybeUpdateGenotypeResults();
if (previousVersion != null) {
final context = PharMeApp.navigatorKey.currentContext;
if (context != null) {
Expand Down
27 changes: 4 additions & 23 deletions app/lib/common/utilities/genome_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,11 @@ import 'package:http/http.dart';

import '../module.dart';

Future<void> fetchAndSaveDiplotypesAndActiveDrugs(
String token, String url, ActiveDrugs activeDrugs) async {
if (!shouldFetchDiplotypes()) return;
final response = await getDiplotypes(token, url);
if (response.statusCode == 200) {
await _saveDiplotypeAndActiveDrugsResponse(response, activeDrugs);
} else {
throw Exception();
}
}

Future<Response> getDiplotypes(String? token, String url) async {
return get(Uri.parse(url), headers: {'Authorization': 'Bearer $token'});
}

Future<void> _saveDiplotypeAndActiveDrugsResponse(
Response response,
Future<void> saveDiplotypesAndActiveDrugs(
List<LabResult> labData,
List<String> activeDrugList,
ActiveDrugs activeDrugs,
) async {
// parse response to list of user's labData
final labData =
labDataFromHTTPResponse(response);
final activeDrugList = activeDrugsFromHTTPResponse(response);

UserData.instance.labData = labData;
await UserData.save();
await activeDrugs.setList(activeDrugList);
Expand Down Expand Up @@ -63,7 +44,7 @@ Map<String, GenotypeResult> initializeGenotypeResultKeys() {
return emptyGenotypeResults;
}

Future<void> updateGenotypeResults() async {
Future<void> maybeUpdateGenotypeResults() async {
final skipUpdate = !shouldUpdateGenotypeResults();
if (skipUpdate) return;

Expand Down
2 changes: 1 addition & 1 deletion app/lib/common/widgets/drug_list/cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class DrugListCubit extends Cubit<DrugListState> {

emit(DrugListState.loading());
try {
await updateDrugsWithGuidelines();
await maybeUpdateDrugsWithGuidelines();
await loadDrugs(updateIfNull: false, filter: filter);
} catch (error) {
emit(DrugListState.error());
Expand Down
4 changes: 2 additions & 2 deletions app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"drug_list_subheader_all_drugs": "All medications",
"drug_list_subheader_other_drugs": "Other medications",

"err_could_not_retrieve_access_token": "An unexpected error occurred while retrieving the access token",
"err_could_not_retrieve_access_token": "An unexpected error occurred while logging in",
"err_fetch_user_data_failed": "An error occurred while getting data, please try again later",
"err_generic": "Error",

Expand Down Expand Up @@ -137,7 +137,7 @@
}
},
"drugs_page_tooltip_guideline_present": "{source} guidelines are used to inform the content on this page. These guidelines provide recommendations on which drugs to use based on your DNA.",
"@drugs_page_tooltip_guideline": {
"@drugs_page_tooltip_guideline_present": {
"placeholders": {
"source": {
"type": "String",
Expand Down
91 changes: 18 additions & 73 deletions app/lib/login/cubit.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import 'dart:convert' show jsonDecode;

import 'package:flutter/services.dart';
import 'package:flutter_web_auth/flutter_web_auth.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:http/http.dart' as http;

import '../../common/module.dart';
import 'models/lab.dart';
Expand All @@ -21,23 +16,12 @@ class LoginCubit extends Cubit<LoginState> {
// genomic data from it's endpoint.
Future<void> signInAndLoadUserData(BuildContext context, Lab lab) async {
emit(LoginState.loadingUserData(null));

// authenticate
String? token;
try {
token = await _getAccessToken(
context,
authUrl: lab.authUrl,
tokenUrl: lab.tokenUrl,
);
} on PlatformException catch (e) {
if (e.code == 'CANCELED') {
revertToInitialState();
return;
}
}

if (token == null) {
await lab.authenticate();
} on LabAuthenticationCanceled {
revertToInitialState();
return;
} on LabAuthenticationError {
emit(LoginState.error(
// ignore: use_build_context_synchronously
context.l10n.err_could_not_retrieve_access_token,
Expand All @@ -46,24 +30,22 @@ class LoginCubit extends Cubit<LoginState> {
}

try {
final needNewDataLoad =
shouldFetchDiplotypes() || shouldUpdateGenotypeResults();
// get data
if (needNewDataLoad) {
final loadingMessage = shouldFetchDiplotypes()
// ignore: use_build_context_synchronously
emit(LoginState.loadingUserData(context.l10n.auth_loading_data));
}
await fetchAndSaveDiplotypesAndActiveDrugs(
token, lab.starAllelesUrl.toString(), activeDrugs);
await updateGenotypeResults();

if (!needNewDataLoad) {
? context.l10n.auth_loading_data
// ignore: use_build_context_synchronously
emit(LoginState.loadingUserData(context.l10n.auth_updating_data));
: context.l10n.auth_updating_data;
emit(LoginState.loadingUserData(loadingMessage));
if (shouldFetchDiplotypes()) {
final (labData, activeDrugList) = await lab.loadData();
await saveDiplotypesAndActiveDrugs(
labData,
activeDrugList,
activeDrugs,
);
}
await updateDrugsWithGuidelines();

// login + fetching of data successful
await maybeUpdateGenotypeResults();
await maybeUpdateDrugsWithGuidelines();
MetaData.instance.isLoggedIn = true;
await MetaData.save();
emit(LoginState.loadedUserData());
Expand All @@ -72,43 +54,6 @@ class LoginCubit extends Cubit<LoginState> {
emit(LoginState.error(context.l10n.err_fetch_user_data_failed));
}
}

Future<String> _getAccessToken(
BuildContext context, {
required Uri authUrl,
required Uri tokenUrl,
}) async {
const clientId = 'pharme-app';
const callbackUrlScheme = 'localhost';

// Construct the url
final url = authUrl.replace(queryParameters: {
'response_type': 'code',
'client_id': clientId,
'redirect_uri': '$callbackUrlScheme:/',
'scope': 'openid profile',
});

// Present the dialog to the user
final result = await FlutterWebAuth.authenticate(
url: url.toString(),
callbackUrlScheme: callbackUrlScheme,
);

// Extract code from resulting url
final code = Uri.parse(result).queryParameters['code'];

// Use this code to get an access token
final response = await http.post(tokenUrl, body: {
'client_id': clientId,
'redirect_uri': '$callbackUrlScheme:/',
'grant_type': 'authorization_code',
'code': code,
});

// Get the access token from the response
return jsonDecode(response.body)['access_token'] as String;
}
}

@freezed
Expand Down
44 changes: 29 additions & 15 deletions app/lib/login/models/lab.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
import 'dart:convert';

import 'package:http/http.dart';

import '../../common/module.dart';

class LabAuthenticationCanceled implements Exception {
LabAuthenticationCanceled();
}

class LabAuthenticationError implements Exception {
LabAuthenticationError();
}

class Lab {
Lab({
required this.name,
required this.authUrl,
required this.tokenUrl,
required this.starAllelesUrl,
});

String name;
Uri authUrl;
Uri tokenUrl;
Uri starAllelesUrl;
}

Future<void> authenticate() async {}
Future<(List<LabResult>, List<String>)> loadData() async {
throw UnimplementedError();
}

final labs = [
Lab(
name: 'Mount Sinai Health System',
authUrl: keycloakUrl('realms/pharme/protocol/openid-connect/auth'),
tokenUrl: keycloakUrl('realms/pharme/protocol/openid-connect/token'),
starAllelesUrl: labServerUrl('star-alleles'),
)
];
(List<LabResult>, List<String>) labDataFromHTTPResponse(Response response) {
final json = jsonDecode(response.body) as Map<String, dynamic>;
final labData = json['diplotypes'].map<LabResult>(
LabResult.fromJson
).toList();
var activeDrugs = <String>[];
if (json.containsKey('medications')) {
activeDrugs = List<String>.from(json['medications']);
}
return (labData, activeDrugs);
}
}
Loading

0 comments on commit 5966e70

Please sign in to comment.