Skip to content

Commit

Permalink
refactor(#674): use DrugExtension getters
Browse files Browse the repository at this point in the history
  • Loading branch information
tamslo committed Dec 5, 2023
1 parent 387ebfd commit c783702
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 89 deletions.
36 changes: 13 additions & 23 deletions app/lib/common/models/drug/drug.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hive/hive.dart';

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

part 'drug.g.dart';

Expand Down Expand Up @@ -40,7 +39,7 @@ class Drug {
List<Guideline> guidelines;

@override
bool operator ==(other) => other is Drug && id == other.id;
bool operator == (other) => other is Drug && id == other.id;

@override
int get hashCode => id.hashCode;
Expand All @@ -67,48 +66,39 @@ class DrugAnnotations {
List<String> brandNames;
}

extension DrugIsActive on Drug {
bool isActive() {
return UserData.instance.activeDrugNames?.contains(name) ?? false;
}
}
extension DrugExtension on Drug {
bool get isActive =>
UserData.instance.activeDrugNames?.contains(name) ?? false;

extension DrugMatchesQuery on Drug {
bool matches({required String query, required bool useClass}) {
final namesMatch = name.ilike(query) ||
(annotations.brandNames.any((brand) => brand.ilike(query)));
return useClass
? namesMatch || (annotations.drugclass.ilike(query))
: namesMatch;
}
}

/// Gets the User's matching guideline
extension DrugWithUserGuideline on Drug {
Guideline? userGuideline() => guidelines.firstOrNullWhere(
(guideline) => guideline.lookupkey.all((geneSymbol, geneResults) =>
geneResults.contains(UserData.lookupFor(geneSymbol, drug: name))),
);
}
Guideline? get userGuideline => guidelines.firstOrNullWhere(
(guideline) => guideline.lookupkey.all(
(geneSymbol, geneResults) =>
geneResults.contains(UserData.lookupFor(geneSymbol, drug: name))
),
);

extension DrugGuidelineGenes on Drug {
List<String> get guidelineGenes => guidelines.isNotEmpty
? guidelines.first.lookupkey.keys.toList()
: [];
}

extension DrugWarningLevel on Drug {
WarningLevel get warningLevel =>
userGuideline()?.annotations.warningLevel ?? WarningLevel.none;
userGuideline?.annotations.warningLevel ?? WarningLevel.none;
}

/// Filters for drugs with non-OK warning level
extension CriticalDrugs on List<Drug> {
List<Drug> filterCritical() {
return filter((drug) {
final warningLevel = getWarningLevel(drug.userGuideline());
return warningLevel != WarningLevel.none &&
warningLevel != WarningLevel.green;
return drug.warningLevel != WarningLevel.none &&
drug.warningLevel != WarningLevel.green;
}).toList();
}
}
8 changes: 3 additions & 5 deletions app/lib/common/pages/drug/drug.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ class DrugPage extends StatelessWidget {
}

Widget _buildDrugsPage(BuildContext context, { required bool loading }) {
final userGuideline = drug.userGuideline();
final isActive = drug.isActive();
return pageScaffold(
title: drug.name.capitalize(),
actions: [
Expand All @@ -55,7 +53,7 @@ class DrugPage extends StatelessWidget {
SizedBox(height: 12),
DrugAnnotationCard(
drug,
isActive: isActive,
isActive: drug.isActive,
setActivity: (value) =>
context.read<DrugCubit>().setActivity(drug, value),
disabled: loading,
Expand All @@ -66,11 +64,11 @@ class DrugPage extends StatelessWidget {
tooltip: context.l10n.drugs_page_tooltip_guideline,
),
SizedBox(height: 12),
if (userGuideline != null) ...[
if (drug.userGuideline != null) ...[
Disclaimer(),
SizedBox(height: 12),
],
GuidelineAnnotationCard(userGuideline, drug),
GuidelineAnnotationCard(drug),
],
),
),
Expand Down
34 changes: 17 additions & 17 deletions app/lib/common/pages/drug/widgets/annotation_cards/guideline.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import 'package:url_launcher/url_launcher.dart';

import '../../../../module.dart';
import '../../../../utilities/guideline_utils.dart';
import '../sub_header.dart';

class GuidelineAnnotationCard extends StatelessWidget {
const GuidelineAnnotationCard(this.guideline, this.drug);
const GuidelineAnnotationCard(this.drug);

final Guideline? guideline;
final Drug? drug;
final Drug drug;

@override
Widget build(BuildContext context) {
Expand All @@ -18,7 +16,7 @@ class GuidelineAnnotationCard extends StatelessWidget {
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
_buildHeader(context),
SizedBox(height: 12),
if (guideline != null) ...[
if (drug.userGuideline != null) ...[
_buildCard(context),
SizedBox(height: 8),
Divider(color: PharMeTheme.borderColor),
Expand All @@ -36,25 +34,27 @@ class GuidelineAnnotationCard extends StatelessWidget {
}

Widget _buildCard(BuildContext context) {
final warningLevel = getWarningLevel(guideline);
final upperCardText = guideline?.annotations.implication ??
final upperCardText = drug.userGuideline?.annotations.implication ??
context.l10n.drugs_page_no_guidelines_for_phenotype_implication(
drug!.name
drug.name
);
final lowerCardText = guideline?.annotations.recommendation ??
final lowerCardText = drug.userGuideline?.annotations.recommendation ??
context.l10n.drugs_page_no_guidelines_for_phenotype_recommendation;
return Card(
key: Key('annotationCard'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: warningLevel.color,
color: drug.warningLevel.color,
child: Padding(
padding: EdgeInsets.all(12),
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
Icon(warningLevel.icon, color: PharMeTheme.onSurfaceText),
Icon(
drug.warningLevel.icon,
color: PharMeTheme.onSurfaceText,
),
SizedBox(width: 12),
Flexible(
child: Text(
Expand All @@ -74,17 +74,17 @@ class GuidelineAnnotationCard extends StatelessWidget {
Widget _buildHeader(BuildContext context) {
var headerContent = '';
var headerStyle = PharMeTheme.textTheme.bodyLarge!;
if (guideline == null && drug!.guidelines.isEmpty) {
headerContent = context.l10n.drugs_page_guidelines_empty(drug!.name);
if (drug.userGuideline == null && drug.guidelines.isEmpty) {
headerContent = context.l10n.drugs_page_guidelines_empty(drug.name);
headerStyle = headerStyle.copyWith(fontStyle: FontStyle.italic);
} else {
final genes = guideline?.lookupkey.keys ??
drug!.guidelines.first.lookupkey.keys;
final genes = drug.userGuideline?.lookupkey.keys ??
drug.guidelines.first.lookupkey.keys;
final geneDescriptions = genes.map((geneSymbol) {
final phenotypeInformation = UserData.phenotypeFor(
geneSymbol,
context,
drug: drug?.name,
drug: drug.name,
);
var description = '$geneSymbol: ${phenotypeInformation.phenotype}';
if (phenotypeInformation.adaptionText.isNotNullOrBlank) {
Expand All @@ -108,7 +108,7 @@ class GuidelineAnnotationCard extends StatelessWidget {
// pipes are illegal characters in URLs so please
// - forgive the cheap hack or
// - refactor by making a custom object and defining equality for it :)
final sources = guideline!.externalData
final sources = drug.userGuideline!.externalData
.map((data) => '${data.source}|${data.guidelineUrl}')
.toSet();
return Column(children: [
Expand Down
15 changes: 0 additions & 15 deletions app/lib/common/utilities/guideline_utils.dart

This file was deleted.

12 changes: 5 additions & 7 deletions app/lib/common/utilities/pdf_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,7 @@ String _userInfoPerGene(
BuildContext buildContext,
) {
if (drug.guidelines.isEmpty) return buildContext.l10n.pdf_no_value;
final guidelineGenes = drug.guidelines.first.lookupkey.keys.toList();
return guidelineGenes.map((gene) =>
return drug.guidelineGenes.map((gene) =>
'$gene: ${
getInfo(gene, drug, buildContext) ?? buildContext.l10n.pdf_no_value
}'
Expand All @@ -183,7 +182,6 @@ List<pw.Widget> _buildUserPart(
Drug drug,
Font emoji,
) {
final userGuideline = drug.userGuideline();
final patientGenotype = _userInfoPerGene(
drug,
(gene, drug, context) => UserData.genotypeFor(gene),
Expand All @@ -210,11 +208,11 @@ List<pw.Widget> _buildUserPart(
'green': '✅',
null: '❔',
};
final implication = userGuideline?.annotations.implication ??
final implication = drug.userGuideline?.annotations.implication ??
buildContext.l10n.drugs_page_no_guidelines_for_phenotype_implication(
drug.name
);
final recommendation = userGuideline?.annotations.recommendation ??
final recommendation = drug.userGuideline?.annotations.recommendation ??
buildContext.l10n.drugs_page_no_guidelines_for_phenotype_recommendation;
return [
_buildSubheading(buildContext.l10n.pdf_heading_user_data),
Expand Down Expand Up @@ -251,7 +249,7 @@ List<pw.Widget> _buildUserPart(
child: _PdfDescription(
title: buildContext.l10n.pdf_user_guideline,
text: '$implication $recommendation',
icon: warningLevelIcons[userGuideline?.annotations.warningLevel.name],
icon: warningLevelIcons[drug.warningLevel.name],
emojiFont: emoji,
),
),
Expand All @@ -262,7 +260,7 @@ List<pw.Widget> _buildExternalGuidelinePart(
Drug drug,
BuildContext buildContext
) {
final externalData = drug.userGuideline()?.externalData;
final externalData = drug.userGuideline?.externalData;
final heading = _buildSubheading(
buildContext.l10n.pdf_heading_clinical_guidelines
);
Expand Down
12 changes: 3 additions & 9 deletions app/lib/common/widgets/drug_list/cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,24 +125,18 @@ class FilterState {
bool isAccepted(Drug drug, ActiveDrugs activeDrugs, {
required bool useDrugClass,
}) {
final userGuideline = drug.userGuideline();
final guidelineGenes = drug.guidelines.isNotEmpty ?
drug.guidelines.first.lookupkey.keys.toList() :
[];
final warningLevel =
userGuideline?.annotations.warningLevel ?? WarningLevel.none;
var warningLevelMatches = showWarningLevel[warningLevel] ?? true;
var warningLevelMatches = showWarningLevel[drug.warningLevel] ?? true;
// WarningLevel.none is also shown in green in app; therefore, it should
// also be filtered with green option
if (warningLevel == WarningLevel.none) {
if (drug.warningLevel == WarningLevel.none) {
warningLevelMatches = warningLevelMatches &&
showWarningLevel[WarningLevel.green]!;
}
final isDrugAccepted =
drug.matches(query: query, useClass: useDrugClass) &&
(activeDrugs.contains(drug.name) || showInactive) &&
warningLevelMatches &&
(gene.isBlank || (guidelineGenes.contains(gene)));
(gene.isBlank || (drug.guidelineGenes.contains(gene)));
return isDrugAccepted;
}

Expand Down
12 changes: 4 additions & 8 deletions app/lib/common/widgets/drug_list/drug_items/drug_cards.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import '../../../module.dart';
import '../../../utilities/guideline_utils.dart';
import 'utils.dart';

List<Widget> buildDrugCards(
Expand All @@ -10,11 +9,9 @@ List<Widget> buildDrugCards(
bool showDrugInteractionIndicator = false,
}
) {
int warningLevelSeverity(Drug drug) =>
getWarningLevel(drug.userGuideline()).severity;
drugs.sort((drugA, drugB) {
final warningLevelComparison = -warningLevelSeverity(drugA)
.compareTo(warningLevelSeverity(drugB));
final warningLevelComparison = -drugA.warningLevel.severity
.compareTo(drugB.warningLevel.severity);
if (warningLevelComparison == 0) {
return drugA.name.compareTo(drugB.name);
}
Expand Down Expand Up @@ -45,15 +42,14 @@ class DrugCard extends StatelessWidget {

@override
Widget build(BuildContext context) {
final warningLevel = getWarningLevel(drug.userGuideline());
final drugName = formatDrugName(drug, showDrugInteractionIndicator);
return Padding(
padding: EdgeInsets.symmetric(vertical: PharMeTheme.smallSpace / 2),
child: RoundedCard(
onTap: onTap,
padding: EdgeInsets.all(8),
radius: 16,
color: warningLevel.color,
color: drug.warningLevel.color,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expand All @@ -62,7 +58,7 @@ class DrugCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
Icon(warningLevel.icon),
Icon(drug.warningLevel.icon),
SizedBox(width: 4),
Text(
drugName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ List<Widget> buildDrugCheckboxList(
}
) {
if (buildParams == null) throw Exception();
final activeDrugs = drugs.filter((drug) => drug.isActive()).toList();
final activeDrugs = drugs.filter((drug) => drug.isActive).toList();
final activeDrugsList = activeDrugs.isEmpty
? [Padding(
padding: EdgeInsets.all(PharMeTheme.mediumSpace),
Expand Down Expand Up @@ -57,7 +57,7 @@ List<CheckboxListTile> _buildCheckboxList(
(drug) => CheckboxListTile(
key: Key('drug-checkbox-tile-${drug.name}-$keyPrefix'),
enabled: checkboxesEnabled,
value: drug.isActive(),
value: drug.isActive,
onChanged: (value) => onCheckboxChange(drug, value),
title: Text(formatDrugName(drug, showDrugInteractionIndicator)),
subtitle: (drug.annotations.brandNames.isNotEmpty) ?
Expand Down
10 changes: 7 additions & 3 deletions app/lib/report/pages/report.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'package:provider/provider.dart';
import '../../common/models/drug/cached_drugs.dart';
import '../../common/module.dart';
import '../../common/utilities/color_utils.dart';
import '../../common/utilities/guideline_utils.dart';

class ReportPage extends StatelessWidget {
@override
Expand All @@ -16,10 +15,15 @@ class ReportPage extends StatelessWidget {

Widget _buildReportPage(BuildContext context, ActiveDrugs activeDrugs) {
final hasActiveInhibitors = activeDrugs.names.any(isInhibitor);
final guidelineGenes = getGuidelineGenes();
final genes = <String>{};
for (final drug in CachedDrugs.instance.drugs!) {
for (final guideline in drug.guidelines) {
guideline.lookupkey.keys.forEach(genes.add);
}
}

final notTestedString = context.l10n.general_not_tested;
final userPhenotypes = guidelineGenes.map(
final userPhenotypes = genes.map(
(geneSymbol) => UserData.instance.lookups![geneSymbol] ??
CpicPhenotype(
geneSymbol: geneSymbol,
Expand Down

0 comments on commit c783702

Please sign in to comment.