Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,380 changes: 2,380 additions & 0 deletions EMSDataSet.sch

Large diffs are not rendered by default.

15,629 changes: 15,629 additions & 0 deletions EMSDataSet.sch.xsl

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ <h3>{{ record.dataSet }}</h3>
</div>
<div class="mb-3">
<button (click)="isEditing = true" class="btn btn--sm btn--primary">Edit</button>
<a class="btn btn--sm btn--secondary ms-2" [download]="record.id + '.json'" [href]="'/api/interfaces/' + record.id + '/export'"
>Export</a
>
</div>
<div class="table-responsive">
<table class="table">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<router-outlet></router-outlet>
<div [class.d-none]="route.firstChild">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3">
<h1 class="h2">Edit Section</h1>
<h1 class="h2">Edit Element</h1>
</div>
<shared-form
*ngIf="id"
Expand All @@ -11,6 +11,8 @@ <h1 class="h2">Edit Section</h1>
(cancel)="onCancel()"
(update)="onUpdate()"
(delete)="onDelete()"
[preTransformRecord]="preTransformRecord"
[transformRecord]="transformRecord"
class="d-block col-xl-6">
<ng-template let-record="record" let-error="error">
<admin-interfaces-element-form [interfaceId]="interfaceId" [record]="record" [error]="error"></admin-interfaces-element-form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,18 @@ export class EditInterfaceElementComponent implements OnInit {
onDelete() {
this.onCancel();
}

preTransformRecord(record: any) {
let newRecord = { ...record };
newRecord.visibleIf = record.visibleIf ? JSON.stringify(record.visibleIf) : null;
newRecord.onChange = record.onChange ? JSON.stringify(record.onChange) : null;
return newRecord;
}

transformRecord(record: any) {
let newRecord = { ...record };
newRecord.visibleIf = record.visibleIf ? JSON.parse(record.visibleIf) : null;
newRecord.onChange = record.onChange ? JSON.parse(record.onChange) : null;
return newRecord;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,18 @@
<div class="mb-3">
<shared-text-field id="customId" label="Custom ID" [source]="record" [target]="record" [error]="error"></shared-text-field>
</div>
<div class="mb-3">
<shared-text-field id="name" label="Name" [source]="record" [target]="record" [error]="error"></shared-text-field>
</div>
<div class="mb-3">
<shared-text-field id="shortName" label="Short Name" [source]="record" [target]="record" [error]="error"></shared-text-field>
</div>
<div class="mb-3">
<shared-text-field id="unit" label="Unit" [source]="record" [target]="record" [error]="error"></shared-text-field>
</div>
<div class="mb-3">
<shared-text-field id="visibleIf" label="Visible If" [source]="record" [target]="record" [error]="error" [rows]="2"></shared-text-field>
</div>
<div class="mb-3">
<shared-text-field id="onChange" label="On Change" [source]="record" [target]="record" [error]="error" [rows]="2"></shared-text-field>
</div>
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { pick } from 'lodash-es';

import { NavigationService } from 'shared';

Expand All @@ -24,10 +23,13 @@ export class NewInterfaceElementComponent implements OnInit {
this.interfaceId = this.route.snapshot.parent?.parent?.parent?.params['id'];
}

transformRecord = (record: any) => ({
...pick(record, ['nemsisElementId', 'screenId', 'position', 'column', 'customId']),
sectionId: this.sectionId,
});
transformRecord = (record: any) => {
let newRecord = { ...record };
newRecord.visibleIf = record.visibleIf ? JSON.parse(record.visibleIf) : null;
newRecord.onChange = record.onChange ? JSON.parse(record.onChange) : null;
newRecord.sectionId = this.sectionId;
return newRecord;
};

onCreate(record: any) {
this.navigation.backTo(`/interfaces/${this.interfaceId}/screens/${this.screenId}/sections/${this.sectionId}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
<h1 class="h2">Edit Screen</h1>
</div>
<shared-form
#form
*ngIf="id"
type="screens"
[id]="id"
[hideButtons]="!isEditing"
[showCancel]="true"
(cancel)="isEditing = false"
(update)="isEditing = false"
(update)="isEditing = false; form.refresh()"
(delete)="onDelete()"
class="d-block col-xl-6">
<ng-template let-record="record" let-error="error">
Expand All @@ -22,6 +23,14 @@ <h1 class="h2">Edit Screen</h1>
<label>Name</label>
<h3>{{ record.name }}</h3>
</div>
<div *ngIf="record.position" class="mb-3">
<label>Position</label>
<h3>{{ record.position }}</h3>
</div>
<div *ngIf="record.nemsisElement" class="mb-3">
<label>NEMSIS Element</label>
<h3>{{ record.nemsisElement.name }}</h3>
</div>
<div class="mb-3">
<button (click)="isEditing = true" class="btn btn--sm btn--primary">Edit</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
<div class="mb-3">
<shared-text-field id="name" label="Name" [source]="record" [target]="record" [error]="error"></shared-text-field>
</div>
<div class="mb-3">
<shared-text-field type="number" id="position" label="Position" [source]="record" [target]="record" [error]="error"></shared-text-field>
</div>
<div class="mb-3">
<shared-object-field
id="nemsisElementId"
label="NEMSIS Element"
[source]="record"
[target]="record"
[error]="error"
objectNameProperty="name"
objectApiPath="nemsisElements"
[objectApiSearchParams]="nemsisElementSearchParams"
[inputFormatter]="nemsisElementFormatter"></shared-object-field>
</div>
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
import { Component, Input } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { HttpParams, HttpResponse } from '@angular/common/http';
import { ActivatedRoute } from '@angular/router';

import { ApiService } from 'shared';

@Component({
selector: 'admin-interfaces-screen-form',
templateUrl: './interface-screen-form.component.html',
standalone: false,
})
export class InterfaceScreenFormComponent {
export class InterfaceScreenFormComponent implements OnInit {
@Input() record: any = null;
@Input() error: any = null;

interfaceId?: string;
nemsisElementSearchParams = new HttpParams();
nemsisElementFormatter = (result: any) => `${result.displayName} (${result.name})`;

constructor(
private api: ApiService,
private route: ActivatedRoute,
) {}

ngOnInit() {
this.interfaceId = this.route.snapshot.parent?.params['id'];
this.api.interfaces.get(this.interfaceId ?? '').subscribe((res: HttpResponse<any>) => {
const { nemsisVersion } = res.body;
this.nemsisElementSearchParams = this.nemsisElementSearchParams.set('nemsisVersion', nemsisVersion);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ <h4>National Repository</h4>
</ng-template>
<ng-template #item let-version="item">
<div class="d-flex justify-content-between">
{{ version }}
<a style="font-size: inherit" *ngIf="nemsis.versionsInstalled.includes(version)" [routerLink]="['/nemsis', version]">{{
version
}}</a>
<span *ngIf="!nemsis.versionsInstalled.includes(version)">{{ version }}</span>
<span *ngIf="nemsis.versionsInstalled.includes(version)">
<i class="fas fa-check-circle text-success"></i>
</span>
Expand Down
125 changes: 82 additions & 43 deletions lib/nemsis/emsXsdParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,85 +3,124 @@ const path = require('path');
const { XmlParser } = require('./xmlParser');

class NemsisEmsXsdParser extends XmlParser {
onStartElement(name, attr) {
if (name === 'xs:include') {
this.includes.push(attr.schemaLocation);
getCurrentXsdPath() {
return `/${this.stack
.map((s) => {
let component = s.element;
if (s.attr.id) {
component += `[@id='${s.attr.id}']`;
} else if (s.attr.name) {
component += `[@name='${s.attr.name}']`;
}
return component;
})
.join('/')}`;
}

getCurrentXmlPath() {
let xmlPath =
this.xmlXPathPrefix +
this.nodes.map((n) => `${n.name}${n.maxOccurs === null || (Number.isInteger(n.maxOccurs) && n.maxOccurs > 1) ? `[]` : ''}`).join('/');
if (xmlPath.endsWith('[]')) {
xmlPath = xmlPath.slice(0, -2);
}
if (this.stack.length === 0) {
return xmlPath;
}

onStartElement(element, attr) {
this.stack.push({ xsd: this.xsd, element, attr });
if (element === 'xs:include') {
this.includes.push(attr.schemaLocation);
} else if (this.nodes.length === 0) {
// watch for start element
if (attr.name.startsWith(this.startElement.name)) {
this.stack.push(this.startElement);
if (attr.name?.startsWith(this.startingNode.name)) {
this.nodes.push(this.startingNode);
this.stack[this.stack.length - 1].node = this.startingNode;
this.startingNode.xsdPath = this.getCurrentXsdPath();
this.startingNode.xmlPath = this.getCurrentXmlPath();
}
} else if (name === 'xs:element') {
} else if (element === 'xs:element') {
const minOccurs = parseInt(attr.minOccurs ?? '1', 10);
const maxOccurs = parseInt(attr.maxOccurs ?? '1', 10);
const node = {
name: attr.name,
xsd: this.xsd,
name: attr.name,
minOccurs,
maxOccurs: Number.isNaN(maxOccurs) ? null : maxOccurs,
xsdPath: '',
xmlPath: '',
children: [],
};
if (attr.nillable) {
node.isNillable = attr.nillable === 'true';
}
for (let i = this.stack.length - 1; i >= 0; i -= 1) {
if (this.stack[i].name) {
this.stack[i].children.push(node);
for (let i = this.nodes.length - 1; i >= 0; i -= 1) {
if (this.nodes[i].name) {
this.nodes[i].children?.push(node);
break;
}
}
this.stack.push(node);
} else {
this.stack.push({ attr });
this.nodes.push(node);
this.stack[this.stack.length - 1].node = node;
node.xsdPath = this.getCurrentXsdPath();
node.xmlPath = this.getCurrentXmlPath();
}
}

onEndElement(name) {
if (this.stack.length > 0) {
const { _text } = this.stack[this.stack.length - 1];
let element;
for (let i = this.stack.length - 2; i >= 0; i -= 1) {
if (this.stack[i].name) {
element = this.stack[i];
break;
}
}
if ((name === 'definition' || name === 'xs:documentation') && element && _text) {
element.definition = _text;
} else if (name === 'name' && element && _text) {
element.displayName = _text;
} else if (name === 'national' && element) {
element.isNational = _text === 'Yes';
} else if (name === 'state' && element) {
element.isState = _text === 'Yes';
} else if (name === 'usage' && element) {
element.usage = _text;
}
delete this.stack[this.stack.length - 1]._text;
this.stack.pop();
if (this.stack.length === 0) {
this.resolve();
onEndElement(element) {
const { _text } = this.stack[this.stack.length - 1];
let node;
for (let i = this.stack.length - 2; i >= 0; i -= 1) {
if (this.stack[i].node) {
({ node } = this.stack[i]);
break;
}
}
if ((element === 'definition' || element === 'xs:documentation') && node && _text) {
node.definition = _text;
} else if (element === 'name' && node && _text) {
node.displayName = _text;
} else if (element === 'national' && node) {
node.isNational = _text === 'Yes';
} else if (element === 'state' && node) {
node.isState = _text === 'Yes';
} else if (element === 'usage' && node) {
node.usage = _text;
}
if (this.stack[this.stack.length - 1].node) {
this.nodes.pop();
}
this.stack.pop();
if (this.stack.length === 0) {
this.resolve();
}
}

async parsePatientCareReport() {
this.includes = [];
this.nodes = [];
this.xsd = path.basename(this.filePath);
this.result = { xsd: this.xsd, name: 'PatientCareReport', children: [] };
this.startElement = this.result;
this.result = {
xsd: this.xsd,
name: 'PatientCareReport',
xsdPath: '',
xmlPath: '',
children: [],
};
this.startingNode = this.result;
this.xmlXPathPrefix = '/';
await this.parse((_, parser) => {
parser.on('startElement', (name, attr) => this.onStartElement(name, attr));
parser.on('endElement', (name) => this.onEndElement(name));
});
this.xmlXPathPrefix = '/PatientCareReport/';
for (const child of this.result.children) {
const xsd = this.includes.find((i) => i.startsWith(child.name));
if (xsd) {
this.filePath = path.resolve(path.dirname(this.filePath), xsd);
this.xsd = xsd;
this.startElement = child;
this.startElement.xsd = xsd;
this.startingNode = child;
this.startingNode.xsd = xsd;
// eslint-disable-next-line no-await-in-loop
await this.parse((_, parser) => {
parser.on('startElement', (name, attr) => this.onStartElement(name, attr));
Expand Down
Loading