diff --git a/README.md b/README.md index bf90d4b..9cd7e2b 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,15 @@ To run these demos locally: `ng serve` Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. + +## Regional specific configuration + +Some regions may have specific requirements and additional configuration - this configuration needs to be specified when running `ng serve` + +These configuration files are at at `src/environments` and applies to various components within the SCT Demonstrator application. + +Run: +`ng serve --configuration=some-region` + +Note: Adding new regional config must have an accompanying configuration section for both `build` and `serve` in `angular.json`. + diff --git a/angular.json b/angular.json index 3aa290e..fed6f28 100644 --- a/angular.json +++ b/angular.json @@ -93,6 +93,33 @@ "extractLicenses": false, "sourceMap": true, "namedChunks": true + }, + "australia": { + "baseHref": "/", + "budgets": [ + { + "type": "initial", + "maximumWarning": "10mb", + "maximumError": "10mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "500kb", + "maximumError": "999kb" + } + ], + "outputHashing": "all", + "optimization": { + "scripts": true, + "styles": true, + "fonts": false + }, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.australia.ts" + } + ] } }, "defaultConfiguration": "production" @@ -101,9 +128,13 @@ "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { + "buildTarget": "sct-implementation-demonstrator:build:production" }, "development": { "buildTarget": "sct-implementation-demonstrator:build:development" + }, + "australia": { + "buildTarget": "sct-implementation-demonstrator:build:australia" } }, "defaultConfiguration": "development" diff --git a/docs/assets/language/national-language-metadata.json b/docs/assets/language/national-language-metadata.json index 7d154fb..7d129f5 100644 --- a/docs/assets/language/national-language-metadata.json +++ b/docs/assets/language/national-language-metadata.json @@ -111,6 +111,23 @@ "languageDialects": "sv-X-83461000052100,sv-X-46011000052107,en-X-900000000000509007" } ] + }, + { + "title": "Australian Edition", + "moduleUri": "http://snomed.info/sct/32506021000036107", + "languageCodes": [ + "en" + ], + "languageRefsets": [ + "32570271000036106" + ], + "contexts": [ + { + "name": "Australian English", + "languageDialects": "en-X-sctlang-32570271-00003610-6" + } + + ] } ] } \ No newline at end of file diff --git a/src/app/allergies/allergies-allergy-list/allergies-allergy-list-reaction/allergies-allergy-list-reaction.component.html b/src/app/allergies/allergies-allergy-list/allergies-allergy-list-reaction/allergies-allergy-list-reaction.component.html index b3a6a6c..89d06fc 100644 --- a/src/app/allergies/allergies-allergy-list/allergies-allergy-list-reaction/allergies-allergy-list-reaction.component.html +++ b/src/app/allergies/allergies-allergy-list/allergies-allergy-list-reaction/allergies-allergy-list-reaction.component.html @@ -24,19 +24,22 @@ {{option.display}} -
-
- + +
+
+
+ +
+ + + +

Terminology binding:

+
{{ routeBinding.ecl }}
+
+
- - - -

Terminology binding:

-
{{ routeBinding.ecl }}
-
-
diff --git a/src/app/allergies/allergies-allergy-list/allergies-allergy-list-reaction/allergies-allergy-list-reaction.component.ts b/src/app/allergies/allergies-allergy-list/allergies-allergy-list-reaction/allergies-allergy-list-reaction.component.ts index db50f24..71d1ec0 100644 --- a/src/app/allergies/allergies-allergy-list/allergies-allergy-list-reaction/allergies-allergy-list-reaction.component.ts +++ b/src/app/allergies/allergies-allergy-list/allergies-allergy-list-reaction/allergies-allergy-list-reaction.component.ts @@ -1,6 +1,8 @@ import { Component, EventEmitter, Input, Output, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { environment } from '../../../../environments/environment'; + @Component({ selector: 'app-allergies-allergy-list-reaction', templateUrl: './allergies-allergy-list-reaction.component.html', @@ -21,14 +23,17 @@ export class AllergiesAllergyListReactionComponent implements ControlValueAccess // add emitter for new problem @Output() newManifestation = new EventEmitter(); + //config + showExposureRoute = environment.allergyList.enableExposureRoute; + severityOptions = [ { code: 'mild', display: 'Mild', sctCode: '255604002', sctDisplay: 'Mild (qualifier value)' }, { code: 'moderate', display: 'Moderate', sctCode: '6736007', sctDisplay: 'Moderate (qualifier value)' }, { code: 'severe', display: 'Severe', sctCode: '24484000', sctDisplay: 'Severe (qualifier value)' } ]; selectedSeverity: any = {}; - reactionManifestationBinding = { ecl: '<<404684003 |Clinical finding|', title: 'Reaction Manifestation' }; - routeBinding = { ecl: '<<284009009 |Route of administration value|', title: 'Exposure Route' }; + reactionManifestationBinding = environment.allergyList.reactionManifestationBinding; + routeBinding = environment.allergyList.routeBinding; reaction: any = {}; diff --git a/src/app/allergies/allergies-allergy-list/allergies-allergy-list.component.html b/src/app/allergies/allergies-allergy-list/allergies-allergy-list.component.html index b5eb1a4..7b583a4 100644 --- a/src/app/allergies/allergies-allergy-list/allergies-allergy-list.component.html +++ b/src/app/allergies/allergies-allergy-list/allergies-allergy-list.component.html @@ -36,8 +36,16 @@

Allergy list

+ +
+ + Patient Reference + + +
+
-
+
@@ -106,6 +114,14 @@

Allergy list

+ +
+ + Notes + + +
+
diff --git a/src/app/allergies/allergies-allergy-list/allergies-allergy-list.component.ts b/src/app/allergies/allergies-allergy-list/allergies-allergy-list.component.ts index aaa991e..c0eaa1f 100644 --- a/src/app/allergies/allergies-allergy-list/allergies-allergy-list.component.ts +++ b/src/app/allergies/allergies-allergy-list/allergies-allergy-list.component.ts @@ -7,6 +7,9 @@ import { saveAs } from 'file-saver'; import { Clipboard } from '@angular/cdk/clipboard'; import { MatSnackBar } from '@angular/material/snack-bar'; import { SnackAlertComponent } from 'src/app/alerts/snack-alert'; + +import { environment } from '../../../environments/environment'; + @Component({ selector: 'app-allergies-allergy-list', templateUrl: './allergies-allergy-list.component.html', @@ -17,6 +20,11 @@ export class AllergiesAllergyListComponent implements OnInit { @Output() newProblem = new EventEmitter(); + //config + showNotes = environment.allergyList.enableNotes; + showPropensity = environment.allergyList.enablePropensity; + showPatient = environment.allergyList.enablePatient; + clinicalStatusOptions = [ { system: "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", code: 'active', display: 'Active' }, { system: "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical", code: 'inactive', display: 'Inactive' }, @@ -62,13 +70,13 @@ export class AllergiesAllergyListComponent implements OnInit { ]; selectedSeverity: any = {}; - codeBinding = { ecl: '<<418038007 |Propensity to adverse reactions to substance| OR <<420134006 |Propensity to adverse reaction (finding)|', title: 'Allergy/Intolerance by propensity' }; + codeBinding = environment.allergyList.codeBinding; selectedCode: any = null; selectedCodeTerm = ""; recordPropensity = false; - substanceBinding = { ecl: '<<105590001 | Substance (substance) | OR <<373873005 | Pharmaceutical / biologic product (product) |', title: 'Allergy/Intolerance substance or product' }; - refinedSubstanceBinding = { ecl: '<<105590001 | Substance (substance) |', title: 'Allergy/Intolerance substance based on propensity' }; + substanceBinding = environment.allergyList.substanceBinding; + refinedSubstanceBinding = environment.allergyList.refinedSubstanceBinding; selectedSubstanceTerm = ""; selectedSubstance: any = null; @@ -81,13 +89,17 @@ export class AllergiesAllergyListComponent implements OnInit { } ]; - reactionManifestationBinding = { ecl: '<<404684003 |Clinical finding|', title: 'Reaction Manifestation' }; selectedReactionManifestation: any = null; selectedReactionManifestationTerm = ""; - routeBinding = { ecl: '<<284009009 |Route of administration value|', title: 'Exposure Route' }; + selectedRoute: any = null; selectedRouteTerm = ""; + notes: any[] = []; + noteText: string = ""; + + patientReference: string = ""; + outputAllergyBase: any = { "resourceType" : "AllergyIntolerance", "id" : "medication", @@ -117,7 +129,7 @@ export class AllergiesAllergyListComponent implements OnInit { "severity" : "" }], "patient" : { - "reference" : "Patient/example" + "reference" : "" }, "recordedDate" : "2010-03-01", "participant" : [{ @@ -131,7 +143,11 @@ export class AllergiesAllergyListComponent implements OnInit { "actor" : { "reference" : "Practitioner/example" } - }] + }], + "note": [{ + "text": "" + } + ], } outputAllergy: any = JSON.parse(JSON.stringify(this.outputAllergyBase)); @@ -173,6 +189,9 @@ export class AllergiesAllergyListComponent implements OnInit { route: {} } ]; + this.notes = []; + this.noteText = ""; + this.patientReference this.outputAllergy = JSON.parse(JSON.stringify(this.outputAllergyBase)); this.updateAllergyStr(); setTimeout(() => { @@ -190,8 +209,8 @@ export class AllergiesAllergyListComponent implements OnInit { this.outputAllergy.criticality = (this.selectedCriticality?.code) ? [this.selectedCriticality.code] : {}; this.outputAllergy.reaction = []; this.selectedReactions.forEach((reaction: any) => { - if (reaction.manifestation.code) { reaction.manifestation.system = 'http://snomed.info/sct'; } - if (reaction.route.code) { reaction.route.system = 'http://snomed.info/sct'; } + if (reaction.manifestation && reaction.manifestation.code) { reaction.manifestation.system = 'http://snomed.info/sct'; } + if (reaction.route && reaction.route.code) { reaction.route.system = 'http://snomed.info/sct'; } const newReaction = { substance: [{ coding: [this.selectedSubstance] @@ -206,6 +225,15 @@ export class AllergiesAllergyListComponent implements OnInit { }; this.outputAllergy.reaction.push(newReaction); }); + + // Update notes array from noteText + this.notes = this.noteText && this.noteText.trim() !== '' + ? [{ text: this.noteText }] + : []; + this.outputAllergy.note = [...this.notes]; + + this.outputAllergy.patient.reference = this.patientReference.trim() ? this.patientReference : ''; + setTimeout(() => { this.outputAllergyStr = JSON.stringify(this.outputAllergy, null, 2); } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 89f8376..61417d4 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -9,6 +9,8 @@ import { LanguageConfigComponent } from './util/language-config/language-config. import { catchError, of, skip, Subject, switchMap, tap } from 'rxjs'; import { HttpClient } from '@angular/common/http'; +import { environment } from '../environments/environment'; + declare let gtag: Function; @Component({ @@ -41,7 +43,7 @@ export class AppComponent { { name: "SNOMED Dev 1", url: "https://dev-browser.ihtsdotools.org/fhir"}, { name: "Implementation Demo", url: "https://implementation-demo.snomedtools.org/fhir"}, ]; - selectedServer = this.fhirServers[1]; + selectedServer = this.fhirServers[environment.defaultFhirServerIndex]; embeddedMode: boolean = false; demos: any[] = []; @@ -163,18 +165,20 @@ export class AppComponent { } }); + // Set default server from config + this.setFhirServer(this.fhirServers[environment.defaultFhirServerIndex]); + + // now subscribe to changes this.terminologyService.snowstormFhirBase$.subscribe(url => { if (this.fhirServers?.length > 0) { - this.fhirServers.forEach(loopServer => { - if (loopServer.url === url) { - this.selectedServer = loopServer; - this.cdRef.detectChanges(); - this.updateCodeSystemOptions() - } - }); + const found = this.fhirServers.find(server => server.url === url); + if (found) { + this.selectedServer = found; + this.cdRef.detectChanges(); + this.updateCodeSystemOptions(); + } } }); - this.setFhirServer(this.selectedServer); // this.http.get('https://raw.githubusercontent.com/IHTSDO/snomedct-language-metadata/refs/heads/main/national-language-metadata.json').subscribe((data: any) => { // this.languageMetadata = data; @@ -188,7 +192,7 @@ export class AppComponent { setupLanguageMetadata() { let editionUri = this.terminologyService.getFhirUrlParam(); - // Remve the verssion data from the editionUri (http://snomed.info/sct/900000000000207008/version/20250501 to http://snomed.info/sct/900000000000207008) + // Remove the version data from the editionUri (http://snomed.info/sct/900000000000207008/version/20250501 to http://snomed.info/sct/900000000000207008) if (editionUri.includes('/version/')) { const editionUriParts = editionUri.split('/'); editionUriParts.pop(); @@ -200,6 +204,8 @@ export class AppComponent { if (!this.filteredLanguageMetadata) { this.filteredLanguageMetadata = { contexts: [] }; } + + //Fallback to US English if not found this.filteredLanguageMetadata.contexts.push({ name: 'US English', languageDialects: 'en-X-900000000000509007' }); this.setContext(this.filteredLanguageMetadata.contexts[0]); diff --git a/src/app/services/terminology.service.ts b/src/app/services/terminology.service.ts index a594428..bf046e1 100644 --- a/src/app/services/terminology.service.ts +++ b/src/app/services/terminology.service.ts @@ -135,10 +135,20 @@ export class TerminologyService { if (this.context) { return this.context.languageDialects; } else if (this.languageRefsetConcept) { - return this.lang + '-X-' + this.languageRefsetConcept.code + return this.toLanguageCode(this.lang, this.languageRefsetConcept.code); } else return this.lang; } + toLanguageCode(lang: string, langRefset: string) { + if (langRefset.length > 16) { + return `${lang}-x-sctlang-${langRefset.substring(0, 8)}-${langRefset.substring(8, 16)}-${langRefset.substring(16)}`; + } else if (langRefset.length > 8) { + return `${lang}-x-sctlang-${langRefset.substring(0, 8)}-${langRefset.substring(8)}`; + } else { + return `${lang}-x-sctlang-${langRefset}`; + } + } + getCodeSystems() { let requestUrl = `${this.snowstormFhirBase}/CodeSystem`; if (this.snowstormFhirBase.includes('ontoserver')) { diff --git a/src/assets/language/national-language-metadata.json b/src/assets/language/national-language-metadata.json index 7d154fb..7d129f5 100644 --- a/src/assets/language/national-language-metadata.json +++ b/src/assets/language/national-language-metadata.json @@ -111,6 +111,23 @@ "languageDialects": "sv-X-83461000052100,sv-X-46011000052107,en-X-900000000000509007" } ] + }, + { + "title": "Australian Edition", + "moduleUri": "http://snomed.info/sct/32506021000036107", + "languageCodes": [ + "en" + ], + "languageRefsets": [ + "32570271000036106" + ], + "contexts": [ + { + "name": "Australian English", + "languageDialects": "en-X-sctlang-32570271-00003610-6" + } + + ] } ] } \ No newline at end of file diff --git a/src/environments/environment.australia.ts b/src/environments/environment.australia.ts new file mode 100644 index 0000000..7992946 --- /dev/null +++ b/src/environments/environment.australia.ts @@ -0,0 +1,33 @@ +export const environment = { + production: false, + country: 'australia', + defaultFhirServerIndex: 5, + + allergyList: { + enableNotes: true, + enablePropensity: false, + enablePatient: true, + enableExposureRoute: false, + + codeBinding: { + ecl: '<<418038007 |Propensity to adverse reactions to substance| OR <<420134006 |Propensity to adverse reaction (finding)|', + title: 'Allergy/Intolerance by propensity' + }, + substanceBinding: { + ecl: '<<105590001 | Substance (substance) | OR <<373873005 | Pharmaceutical / biologic product (product) |', + title: 'Allergy/Intolerance substance or product' + }, + refinedSubstanceBinding: { + ecl: '<<105590001 | Substance (substance) |', + title: 'Allergy/Intolerance substance based on propensity' + }, + reactionManifestationBinding: { + ecl: '<<404684003 | Clinical finding |', + title: 'Reaction Manifestation' + }, + routeBinding: { + ecl: '<<284009009 | Route of administration value |', + title: 'Exposure Route' + } + } +}; \ No newline at end of file diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000..693bd41 --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,34 @@ +export const environment = { + production: false, + country: 'default', + defaultFhirServerIndex: 1, + + allergyList: { + enableNotes: false, + enablePropensity: true, + enablePatient: false, + enableExposureRoute: true, + + codeBinding: { + ecl: '<<418038007 |Propensity to adverse reactions to substance| OR <<420134006 |Propensity to adverse reaction (finding)|', + title: 'Allergy/Intolerance by propensity' + }, + substanceBinding: { + ecl: '<<105590001 | Substance (substance) | OR <<373873005 | Pharmaceutical / biologic product (product) |', + title: 'Allergy/Intolerance substance or product' + }, + refinedSubstanceBinding: { + ecl: '<<105590001 | Substance (substance) |', + title: 'Allergy/Intolerance substance based on propensity' + }, + reactionManifestationBinding: { + ecl: '<<404684003 | Clinical finding |', + title: 'Reaction Manifestation' + }, + routeBinding: { + ecl: '<<284009009 | Route of administration value |', + title: 'Exposure Route' + } + + } +}; \ No newline at end of file