From 96b6cfa86782ca1f595039547f5b35d467a992a3 Mon Sep 17 00:00:00 2001 From: Tamara Slosarek Date: Thu, 5 Sep 2024 16:25:53 +0200 Subject: [PATCH] refactor(#706): use new phenoconversion description in PDF --- app/lib/common/models/drug/drug.dart | 12 + .../common/models/drug/drug_inhibitors.dart | 326 ++++++++++-------- app/lib/common/utilities/pdf_utils.dart | 45 ++- .../widgets/annotation_cards/guideline.dart | 33 +- app/lib/l10n/app_en.arb | 62 +--- app/lib/report/pages/gene.dart | 2 +- pharme.code-workspace | 1 + 7 files changed, 248 insertions(+), 233 deletions(-) diff --git a/app/lib/common/models/drug/drug.dart b/app/lib/common/models/drug/drug.dart index 7d89a9d3..ff240187 100644 --- a/app/lib/common/models/drug/drug.dart +++ b/app/lib/common/models/drug/drug.dart @@ -109,3 +109,15 @@ extension CriticalDrugs on List { }).toList(); } } + +List getDrugsWithBrandNames(List? drugNames) { + return drugNames?.map(getDrugWithBrandNames).toList() ?? []; +} + +String getDrugWithBrandNames(String drugName) { + final drug = CachedDrugs.instance.drugs!.firstWhere( + (drug) => drug.name == drugName + ); + if (drug.annotations.brandNames.isEmpty) return drugName; + return '$drugName (${drug.annotations.brandNames.join(', ')})'; +} diff --git a/app/lib/common/models/drug/drug_inhibitors.dart b/app/lib/common/models/drug/drug_inhibitors.dart index 036735b1..4433a7e0 100644 --- a/app/lib/common/models/drug/drug_inhibitors.dart +++ b/app/lib/common/models/drug/drug_inhibitors.dart @@ -33,18 +33,24 @@ const Map> moderateDrugInhibitors = { }, }; -final inhibitableGenes = List.from({ +// Private helper functions + +final _inhibitableGenes = List.from({ ...strongDrugInhibitors.keys, ...moderateDrugInhibitors.keys, }); final _drugInhibitorsPerGene = { - for (final gene in inhibitableGenes) gene: [ + for (final gene in _inhibitableGenes) gene: [ ...?strongDrugInhibitors[gene]?.keys, ...?moderateDrugInhibitors[gene]?.keys, ] }; +List _inhibitorsFor(String gene) { + return _drugInhibitorsPerGene[gene] ?? []; +} + bool _isInhibitorOfType( String drugName, Map> inhibitorDefinition @@ -54,127 +60,155 @@ bool _isInhibitorOfType( return influencingDrugs.contains(drugName); } -bool isStrongInhibitor(String drugName) { - return _isInhibitorOfType(drugName, strongDrugInhibitors); -} - -bool isModerateInhibitor(String drugName) { +bool _isModerateInhibitor(String drugName) { return _isInhibitorOfType(drugName, moderateDrugInhibitors); } -bool isInhibitor(String drugName) { - var drugIsInhibitor = false; - for (final gene in _drugInhibitorsPerGene.keys) { - final influencingDrugs = _drugInhibitorsPerGene[gene]; - final originalLookup = UserData.lookupFor(gene, drug: drugName, useOverwrite: false); - if (influencingDrugs!.contains(drugName) && originalLookup != '0.0') { - drugIsInhibitor = true; - break; - } - } - return drugIsInhibitor; -} +class _DisplayConfig { + _DisplayConfig({ + required this.partSeparator, + required this.userSalutation, + required this.userGenitive, + required this.useConsult, + }); -List inhibitedGenes(Drug drug) { - return _drugInhibitorsPerGene.keys.filter( - (gene) => _drugInhibitorsPerGene[gene]!.contains(drug.name) - ).toList(); + final String partSeparator; + final String userSalutation; + final String userGenitive; + final bool useConsult; } -List inhibitorsFor(String gene) { - return _drugInhibitorsPerGene[gene] ?? []; +_DisplayConfig _getDisplayConfig( + BuildContext context, + { required bool userFacing } +) { + final displayConfigs = { + true: _DisplayConfig( + partSeparator: '\n\n', + userSalutation: context.l10n.inhibitor_direct_salutation, + userGenitive: context.l10n.inhibitor_direct_salutation_genitive, + useConsult: true, + ), + false: _DisplayConfig( + partSeparator: ' ', + userSalutation: context.l10n.inhibitor_third_person_salutation, + userGenitive: context.l10n.inhibitor_third_person_salutation_genitive, + useConsult: false, + ), + }; + return displayConfigs[userFacing]!; } -String possiblyAdaptedPhenotype( +String _getPhenoconversionConsequence( + BuildContext context, GenotypeResult genotypeResult, - { String? drug } -) { - final originalPhenotype = genotypeResult.phenotypeDisplayString; - if (!isInhibited(genotypeResult, drug: drug)) { - return originalPhenotype; + { + String? drug, + required _DisplayConfig displayConfig, } - final overwrittenLookup = getOverwrittenLookup( +) { + final activeInhibitors = _activeInhibitorsFor( genotypeResult.gene, drug: drug, ); - if (overwrittenLookup == null) { - return '$originalPhenotype$drugInteractionIndicator'; - } - return '$overwritePhenotype$drugInteractionIndicator'; + return activeInhibitors.all(_isModerateInhibitor) + ? context.l10n.inhibitors_consequence_not_adapted( + genotypeResult.geneDisplayString, + displayConfig.userGenitive, + ).capitalize() + : context.l10n.inhibitors_consequence_adapted( + genotypeResult.geneDisplayString, + genotypeResult.phenotype, + displayConfig.userGenitive, + ).capitalize(); } -String getDrugNames(String drugName) { - final drug = CachedDrugs.instance.drugs!.firstWhere( - (drug) => drug.name == drugName - ); - if (drug.annotations.brandNames.isEmpty) return drugName; - return '$drugName (${drug.annotations.brandNames.join(', ')})'; +String _getInhibitorsString( + BuildContext context, + GenotypeResult genotypeResult, + { String? drug } +) { + return context.l10n.inhibitors_tooltip(enumerationWithAnd( + getDrugsWithBrandNames(_activeInhibitorsFor( + genotypeResult.gene, + drug: drug, + )), + context, + )); } -String inhibitionTooltipText( +String _inhibitionTooltipText( BuildContext context, GenotypeResult genotypeResult, - { String? drug } + { + String? drug, + required _DisplayConfig displayConfig, + } ) { - final activeInhibitors = activeInhibitorsFor( - genotypeResult.gene, + final inhibitorsString = _getInhibitorsString( + context, + genotypeResult, drug: drug, ); - final activeInhibitorsWithBrandNames = - activeInhibitors.map(getDrugNames).toList(); - final inhibitorsString = enumerationWithAnd( - activeInhibitorsWithBrandNames, + final consequence = _getPhenoconversionConsequence( context, + genotypeResult, + drug: drug, + displayConfig: displayConfig, ); - final consequence = activeInhibitors.all(isModerateInhibitor) - ? context.l10n.inhibitors_consequence_not_adapted - : context.l10n.inhibitors_consequence_adapted(genotypeResult.phenotype); - return '$consequence\n\n${context.l10n.inhibitors_tooltip(inhibitorsString)}'; + return '$consequence${ + displayConfig.useConsult ? ' ${context.l10n.consult_text}' : '' + }${displayConfig.partSeparator}$inhibitorsString'; } -Table buildDrugInteractionInfoForMultipleGenes( +Table _drugInteractionTemplate( BuildContext context, - List genotypeResults, - { String? drug } + String tooltipText, + _DisplayConfig displayConfig, ) { - var tooltipText = ''; - for (final (index, genotypeResult) in genotypeResults.indexed) { - final separator = index == 0 ? '' : '\n\n'; - // ignore: use_string_buffers - tooltipText = '$tooltipText$separator${ - inhibitionTooltipText(context, genotypeResult, drug: drug) - }'; - } return buildTable([ TableRowDefinition( drugInteractionIndicator, - context.l10n.inhibitor_message, + context.l10n.inhibitor_message( + displayConfig.userSalutation, + displayConfig.userGenitive, + ), tooltip: tooltipText, )], boldHeader: false, ); } -Table buildDrugInteractionInfo( - BuildContext context, - GenotypeResult genotypeResult, - { String? drug } -) { - return buildTable([ - TableRowDefinition( - drugInteractionIndicator, - context.l10n.inhibitor_message, - tooltip: inhibitionTooltipText(context, genotypeResult, drug: drug), - )], - boldHeader: false, - ); +List _activeInhibitorsFor(String gene, { String? drug }) { + return UserData.instance.activeDrugNames == null + ? [] + : UserData.instance.activeDrugNames!.filter( + (activeDrug) => + _inhibitorsFor(gene).contains(activeDrug) && + activeDrug != drug + ).toList(); +} + +// Public helper functions + +bool isInhibitor(String drugName) { + var drugIsInhibitor = false; + for (final gene in _drugInhibitorsPerGene.keys) { + final influencingDrugs = _drugInhibitorsPerGene[gene]; + final originalLookup = UserData.lookupFor(gene, drug: drugName, useOverwrite: false); + if (influencingDrugs!.contains(drugName) && originalLookup != '0.0') { + drugIsInhibitor = true; + break; + } + } + return drugIsInhibitor; } bool isInhibited( GenotypeResult genotypeResult, { String? drug } ) { - final activeInhibitors = activeInhibitorsFor( + final activeInhibitors = _activeInhibitorsFor( genotypeResult.gene, drug: drug, ); @@ -184,70 +218,10 @@ bool isInhibited( return activeInhibitors.isNotEmpty && phenotypeCanBeInhibited; } -List activeInhibitorsFor(String gene, { String? drug }) { - return UserData.instance.activeDrugNames == null - ? [] - : UserData.instance.activeDrugNames!.filter( - (activeDrug) => - inhibitorsFor(gene).contains(activeDrug) && - activeDrug != drug - ).toList(); -} - -PhenotypeInformation phenotypeInformationFor( - GenotypeResult genotypeResult, - BuildContext context, - { - String? drug, - bool thirdPerson = false, - bool useLongPrefix = false, - } -) { - final userSalutation = thirdPerson - ? context.l10n.drugs_page_inhibitor_third_person_salutation - : context.l10n.drugs_page_inhibitor_direct_salutation; - final strongInhibitorTextPrefix = useLongPrefix - ? context.l10n.strong_inhibitor_long_prefix - : context.l10n.gene_page_phenotype.toLowerCase(); - final originalPhenotype = genotypeResult.phenotypeDisplayString; - final activeInhibitors = activeInhibitorsFor( - genotypeResult.gene, - drug: drug, - ); - if (!isInhibited(genotypeResult, drug: drug)) { - return PhenotypeInformation(phenotype: originalPhenotype); - } - final overwrittenLookup = getOverwrittenLookup( - genotypeResult.gene, - drug: drug, - ); - if (overwrittenLookup == null) { - return PhenotypeInformation( - phenotype: originalPhenotype, - adaptionText: context.l10n.drugs_page_moderate_inhibitors( - userSalutation, - enumerationWithAnd( - activeInhibitors, - context - ), - ), - ); - } - final originalPhenotypeText = context.l10n.drugs_page_original_phenotype( - thirdPerson - ? context.l10n.drugs_page_inhibitor_third_person_salutation_genitive - : context.l10n.drugs_page_inhibitor_direct_salutation_genitive, - originalPhenotype, - ); - return PhenotypeInformation( - phenotype: overwritePhenotype, - adaptionText: context.l10n.drugs_page_strong_inhibitors( - strongInhibitorTextPrefix, - userSalutation, - enumerationWithAnd(activeInhibitors, context), - ), - overwrittenPhenotypeText: originalPhenotypeText, - ); +List inhibitedGenes(Drug drug) { + return _drugInhibitorsPerGene.keys.filter( + (gene) => _drugInhibitorsPerGene[gene]!.contains(drug.name) + ).toList(); } MapEntry? getOverwrittenLookup ( @@ -265,3 +239,67 @@ MapEntry? getOverwrittenLookup ( if (lookup == null) return null; return lookup; } + +String possiblyAdaptedPhenotype( + GenotypeResult genotypeResult, + { String? drug } +) { + final originalPhenotype = genotypeResult.phenotypeDisplayString; + if (!isInhibited(genotypeResult, drug: drug)) { + return originalPhenotype; + } + final overwrittenLookup = getOverwrittenLookup( + genotypeResult.gene, + drug: drug, + ); + if (overwrittenLookup == null) { + return '$originalPhenotype$drugInteractionIndicator'; + } + return '$overwritePhenotype$drugInteractionIndicator'; +} + +String inhibitionTooltipText( + BuildContext context, + List genotypeResults, + { + String? drug, + String partSeparator = '\n\n', + bool userFacing = true, + } +) { + final inhibitedGenotypeResults = genotypeResults.filter( + (genotypeResult) => isInhibited(genotypeResult, drug: drug) + ).toList(); + var tooltipText = ''; + for (final (index, genotypeResult) in inhibitedGenotypeResults.indexed) { + final separator = index == 0 ? '' : partSeparator; + // ignore: use_string_buffers + tooltipText = '$tooltipText$separator${ + _inhibitionTooltipText( + context, + genotypeResult, + drug: drug, + displayConfig: _getDisplayConfig(context, userFacing: userFacing), + ) + }'; + } + return tooltipText; +} + +Table buildDrugInteractionInfo( + BuildContext context, + List genotypeResults, + { + String? drug, + } +) { + return _drugInteractionTemplate( + context, + inhibitionTooltipText( + context, + genotypeResults, + drug: drug, + ), + _getDisplayConfig(context, userFacing: false), + ); +} diff --git a/app/lib/common/utilities/pdf_utils.dart b/app/lib/common/utilities/pdf_utils.dart index 08842d9e..b3a43d9d 100644 --- a/app/lib/common/utilities/pdf_utils.dart +++ b/app/lib/common/utilities/pdf_utils.dart @@ -133,23 +133,30 @@ List _buildDrugPart(Drug drug, BuildContext buildContext) { } String? _getPhenotypeInfo(String genotypeKey, Drug drug, BuildContext context) { - final phenotypeInformation = phenotypeInformationFor( - UserData.instance.genotypeResults!.findOrMissing(genotypeKey, context), + final genotypeResult = UserData.instance.genotypeResults!.findOrMissing( + genotypeKey, context, - drug: drug.name, - thirdPerson: true, - useLongPrefix: true, ); - if (phenotypeInformation.adaptionText.isNullOrBlank) { - return phenotypeInformation.phenotype; + if (!isInhibited(genotypeResult, drug: drug.name)) { + return genotypeResult.phenotypeDisplayString; } - var phenotypeInformationText = '${phenotypeInformation.phenotype} (' - '${phenotypeInformation.adaptionText}'; - if (phenotypeInformation.overwrittenPhenotypeText.isNotNullOrBlank) { - phenotypeInformationText = '$phenotypeInformationText; ' - '${phenotypeInformation.overwrittenPhenotypeText!}'; - } - return '$phenotypeInformationText)'; + return possiblyAdaptedPhenotype(genotypeResult, drug: drug.name); +} + +String? _getPhenoconversionInfo(Drug drug, BuildContext context) { + if (drug.guidelines.isEmpty) return null; + final genotypeResults = drug.guidelineGenotypes.map((genotypeKey) => + UserData.instance.genotypeResults!.findOrMissing( + genotypeKey, + context, + ) + ).toList(); + return '$drugInteractionIndicator ${inhibitionTooltipText( + context, + genotypeResults, + drug: drug.name, + userFacing: false, + )}'; } String? _getActivityScoreInfo( @@ -198,11 +205,15 @@ List _buildUserPart( (genotypeKey, drug, context) => UserData.variantFor(genotypeKey), buildContext, ); - final patientPhenotype = _userInfoPerGene( + var patientPhenotype = _userInfoPerGene( drug, _getPhenotypeInfo, buildContext, ); + final phenoconversionInfo = _getPhenoconversionInfo(drug, buildContext); + if (phenoconversionInfo.isNotNullOrBlank) { + patientPhenotype = '$patientPhenotype\n\n$phenoconversionInfo'; + } final patientActivityScore = _userInfoPerGene( drug, _getActivityScoreInfo, @@ -238,14 +249,14 @@ List _buildUserPart( _PdfSegment( child: _PdfDescription( title: buildContext.l10n.gene_page_phenotype, - text: patientPhenotype + text: patientPhenotype, ), ), _buildTextSpacer(), _PdfSegment( child: _PdfDescription( title: buildContext.l10n.gene_page_activity_score, - text: patientActivityScore + text: patientActivityScore, ), ), _buildTextSpacer(), diff --git a/app/lib/drug/widgets/annotation_cards/guideline.dart b/app/lib/drug/widgets/annotation_cards/guideline.dart index 089f5a75..07e05b30 100644 --- a/app/lib/drug/widgets/annotation_cards/guideline.dart +++ b/app/lib/drug/widgets/annotation_cards/guideline.dart @@ -190,33 +190,30 @@ class GuidelineAnnotationCard extends StatelessWidget { ), ]; } else { - var inhibitedGenotypeResults = []; - final geneDescriptions = drug.guidelineGenotypes.map((genotypeKey) { - final genotypeResult = UserData.instance.genotypeResults!.findOrMissing( + final genotypeResults = drug.guidelineGenotypes.map((genotypeKey) => + UserData.instance.genotypeResults!.findOrMissing( genotypeKey, context, - ); - if (isInhibited(genotypeResult, drug: drug.name)) { - inhibitedGenotypeResults = [ - ...inhibitedGenotypeResults, - genotypeResult, - ]; - } - return TableRowDefinition( - genotypeKey, + ) + ).toList(); + final geneDescriptions = genotypeResults.map((genotypeResult) => + TableRowDefinition( + genotypeResult.geneDisplayString, possiblyAdaptedPhenotype( genotypeResult, drug: drug.name, ), - ); - }); + ) + ).toList(); return [ - buildTable(geneDescriptions.toList()), - if (inhibitedGenotypeResults.isNotEmpty) ...[ + buildTable(geneDescriptions), + if (genotypeResults.any( + (genotypeResult) => isInhibited(genotypeResult, drug: drug.name) + )) ...[ SizedBox(height: PharMeTheme.smallSpace), - buildDrugInteractionInfoForMultipleGenes( + buildDrugInteractionInfo( context, - inhibitedGenotypeResults, + genotypeResults, drug: drug.name, ), ], diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 679a629c..d58e84ee 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -103,60 +103,16 @@ } }, - "inhibitor_message": "One or more of the medications you are currently taking may interact with your genetic result", - "inhibitors_consequence_adapted": "Your phenotype was adapted from {originalPhenotype}. Consult your pharmacist or doctor for more information.", - "inhibitors_consequence_per_gene_adapted": "Your {geneName} phenotype was adapted from {originalPhenotype}. Consult your pharmacist or doctor for more information.", - "inhibitors_consequence_not_adapted": "Your phenotype was not adapted but may need to be. Consult your pharmacist or doctor for more information.", - "inhibitors_tooltip": "Current interacting medications: {inhibitors}", + "inhibitor_direct_salutation": "you are", + "inhibitor_direct_salutation_genitive": "your", + "inhibitor_third_person_salutation": "the user is", + "inhibitor_third_person_salutation_genitive": "the user's", + "inhibitor_message": "One or more of the medications {salutation} currently taking may interact with {salutationGenitive} genetic result", + "inhibitors_consequence_adapted": "{salutationGenitive} {geneName} phenotype was adapted from {originalPhenotype}.", + "inhibitors_consequence_not_adapted": "{salutationGenitive} {geneName} phenotype was not adapted but may need to be.", + "inhibitors_tooltip": "Current interacting medications: {inhibitors}.", + "consult_text": "Consult your pharmacist or doctor for more information.", - "drugs_page_inhibitor_direct_salutation": "you are", - "drugs_page_inhibitor_direct_salutation_genitive": "your", - "drugs_page_inhibitor_third_person_salutation": "the user is", - "drugs_page_inhibitor_third_person_salutation_genitive": "the user's", - "drugs_page_moderate_inhibitors": "however, this phenotype may need to be adjusted because {salutation} currently taking {inhibitors}", - "@drugs_page_moderate_inhibitors": { - "placeholders": { - "salutation": { - "type": "String", - "example": "you are" - }, - "inhibitors": { - "type": "String", - "example": "abiraterone, cinacalcet, and duloxetine" - } - } - }, - "strong_inhibitor_long_prefix": "however, this has been", - "drugs_page_strong_inhibitors": "{prefix} adjusted because {salutation} currently taking {inhibitors}", - "@drugs_page_strong_inhibitors": { - "placeholders": { - "prefix": { - "type": "String", - "example": "phenotype" - }, - "salutation": { - "type": "String", - "example": "you are" - }, - "inhibitors": { - "type": "String", - "example": "bupropion and fluoxetine" - } - } - }, - "drugs_page_original_phenotype": "{salutation} DNA-based phenotype is {originalPhenotype}", - "@drugs_page_original_phenotype": { - "placeholders": { - "salutation": { - "type": "String", - "example": "your" - }, - "originalPhenotype": { - "type": "String", - "example": "Normal Metabolizer" - } - } - }, "drugs_page_guidelines_empty": "No guidelines are present for {drugName}", "@drugs_page_guidelines_empty": { "placeholders": { diff --git a/app/lib/report/pages/gene.dart b/app/lib/report/pages/gene.dart index bc30cb32..4256a5c0 100644 --- a/app/lib/report/pages/gene.dart +++ b/app/lib/report/pages/gene.dart @@ -65,7 +65,7 @@ class GenePage extends HookWidget { SizedBox(height: PharMeTheme.smallSpace), buildDrugInteractionInfo( context, - genotypeResult, + [genotypeResult], ), ] ], diff --git a/pharme.code-workspace b/pharme.code-workspace index 014fb7a3..201f44af 100644 --- a/pharme.code-workspace +++ b/pharme.code-workspace @@ -68,6 +68,7 @@ "Metabolizer", "mirabegron", "mocktail", + "noto", "NSAID", "omeprazole", "pantoprazole",