Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to support Generate CCDA endpoint #3

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ <h4>Server: {{connectedServer}}</h4>
<a routerLink="/token" mat-list-item>Access Token</a>
<a routerLink="/user-profile" mat-list-item>User Profile</a>
<app-resources-menu></app-resources-menu>
<a routerLink="/generate-ccda" mat-list-item>Generate CCDA</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav #requestResponseLog opened="false" mode="side" position="end">
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<mat-progress-bar *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>
<mat-card class="mat-elevation-z8">
<mat-card-title>
<div class="row">
<div class="col-md-2 col-xs-1">
<span style="white-space:nowrap;">Generate-CCDA</span>
</div>
<div class="col-xs-3 col-md-9">
</div>
</div>
</mat-card-title>
<mat-card-content>
<mat-accordion>
<mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
Params
</mat-panel-title>
<mat-panel-description>
Specify the filter as JSON [
<a href="https://github.com/FHIR/fhir.js/blob/master/README.md#search">Reference</a>]
</mat-panel-description>
</mat-expansion-panel-header>
<ace-editor mode="text" [(text)]="queryCode" style="height:100px;" [theme]="'eclipse'"></ace-editor>
<div class="min-padding">
<span class="min-padding">
<button mat-raised-button color="primary" (click)="submit()" *ngIf="!invalidJson">Generate</button>
</span>
<button mat-raised-button color="warn" (click)="reset()">Reset</button>
</div>
</mat-expansion-panel>
</mat-accordion>
<div *ngIf="!error && resources">
<mat-tab-group>
<mat-tab label="LIST">
<mat-list>
<div *ngIf="resources.resourceType">Resource type: {{resources.resourceType}}</div>
<div *ngIf="resources.id">Id: {{resources.id}}</div>
<div *ngIf="resources.meta.lastUpdated">Last updated: {{resources.meta.lastUpdated}}</div>
<div *ngIf="resources.content">Number of attachments: {{resources.content.length}}</div>
<div *ngFor="let content of resources.content">
<mat-list-item>
<p mat-line>Attachment title: {{content.attachment.title ? content.attachment.title : 'Untitled'}} </p>
<button mat-raised-button color="primary" (click)="OnDownload(content)">Download</button>
</mat-list-item>
</div>
</mat-list>
</mat-tab>
<mat-tab label="View">
<ngx-json-viewer [json]="resources"></ngx-json-viewer>
</mat-tab>
<mat-tab label="JSON">
<ace-editor mode="text" [text]="resourcesCode" style="height:500px;" [theme]="'eclipse'"></ace-editor>
</mat-tab>
</mat-tab-group>
</div>
</mat-card-content>
</mat-card>
<app-object-viewer *ngIf="error" title="Error" [value]="error" hideView="true"></app-object-viewer>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { GenerateCcdaComponent } from './generate-ccda.component';

describe('GenerateCcdaComponent', () => {
let component: GenerateCcdaComponent;
let fixture: ComponentFixture<GenerateCcdaComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ GenerateCcdaComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(GenerateCcdaComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
155 changes: 155 additions & 0 deletions src/app/components/misc-pages/generate-ccda/generate-ccda.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { Component, OnInit, NgZone, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs';
import { GENERATE_CCDA_SETTINGS } from 'src/app/data/generate-ccda-settings';
import { HelperService, SmartService } from 'src/app/services';
import { CCDSResourceHelperService } from 'src/app/services/ccds-resource-helper.service';

@Component({
selector: 'app-generate-ccda',
templateUrl: './generate-ccda.component.html',
styleUrls: ['./generate-ccda.component.css']
})
export class GenerateCcdaComponent implements OnInit, OnDestroy {
/**
* Query object passed to the SMART JS Client Search api.
*/
query: any;

/**
* Error occured while trying to fetch the resource
*/
error: any;

/**
* Flag to show that a request to fetch FHIR resources are in progress. Used by the Loading indicator.
*/
isLoading: boolean;

/**
* Flag to show whether the json entered in the editor is a valid JSON or not. The save button is hidden based on this flag.
*/
invalidJson: boolean;

/**
* Search Parameters supported by the FHIR Server for this particular FHIR Resource Type
*/
searchParams: any;

/**
* List of resources fetched from the FHIR server based on the query object
*/
resources: any;

private _unsubscribe = new Subject<void>();

constructor(
private _smartService: SmartService,
private _helperService: HelperService,
private _route: ActivatedRoute,
private _zone: NgZone,
private _CCDSResourceHelperService: CCDSResourceHelperService
) { }

ngOnInit() {
this._setSupportedSearchParams();
}

/**
* Set the search parameters supported by the resource type in the filter
*/
private _setSupportedSearchParams() {
this.searchParams = GENERATE_CCDA_SETTINGS.SearchQueryParameters;
this.query = this.searchParams;
}

/**
* Called by the Apply button, to apply the query object in the editor and perform the FHIR API Call
*/
submit() {
this._smartService.getClient()
.takeUntil(this._unsubscribe)
.subscribe(smartClient => {
this.generateCCDA(smartClient);
});
}

/**
* Called by the Reset button in the filter to clear the current query object and reset.
*/
reset() {
this._setSupportedSearchParams();
}

generateCCDA(smartClient: FHIR.SMART.SMARTClient) {
this.isLoading = true;
console.log(this.query);
const searchParams: FHIR.SMART.SearchParams = {
type: GENERATE_CCDA_SETTINGS.Endpoint,
query: this.query
};

// Makes use of the SMART on FHIR JS Client search api method
smartClient.api.search(searchParams).then(response => {
this._zone.run(() => {
this.isLoading = false;
this.resources = response.data;
this.error = null;
});
}, error => {
this._zone.run(() => {
this.isLoading = false;
this.error = error;
});
});
}

OnDownload(content) {
console.log(content);
let attachment = content.attachment;
let data = atob(`${attachment.data}`);
let blob = new Blob([data], { type: `${attachment.contentType}` });
let url = URL.createObjectURL(blob);
let filename = GENERATE_CCDA_SETTINGS.DownloadFileName;
if (url) {
var a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
}

/**
* Setter method used by the Ace Editor to set the value of the query object
*/
set queryCode(v) {
try {
this.query = JSON.parse(v);
this.invalidJson = false;
} catch (e) {
this.invalidJson = true;
console.log('error occored while you were typing the JSON');
}
}

/**
* Getter method used by the Ace Editor to get the value of the query object
*/
get queryCode() {
return JSON.stringify(this.query, null, 2);
}

/**
* Getter method used by the Ace Editor to get the value of the resources object
*/
get resourcesCode() {
return JSON.stringify(this.resources, null, 2);
}

ngOnDestroy() {
this._unsubscribe.next();
this._unsubscribe.complete();
}
}
1 change: 1 addition & 0 deletions src/app/components/misc-pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './launch/launch.component';
export * from './redirect/redirect.component';
export * from './state/state.component';
export * from './user-profile/user-profile.component';
export * from './generate-ccda/generate-ccda.component';
3 changes: 2 additions & 1 deletion src/app/components/misc-pages/misc-pages-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
IndexComponent, StateComponent, AccessTokenComponent, UserProfileComponent,
ConformanceComponent, ConnectComponent, LaunchComponent, RedirectComponent
ConformanceComponent, ConnectComponent, LaunchComponent, RedirectComponent, GenerateCcdaComponent
} from '.';
import { AuthGaurd } from '../../misc/auth-guard';
import { Routes, RouterModule } from '@angular/router';
Expand All @@ -16,6 +16,7 @@ const routes: Routes = [
{ path: 'connect/:uniqueName', component: ConnectComponent },
{ path: 'launch/:uniqueName', component: LaunchComponent },
{ path: 'redirect/:uniqueName', component: RedirectComponent },
{ path: 'generate-ccda', component: GenerateCcdaComponent },
];

@NgModule({
Expand Down
4 changes: 3 additions & 1 deletion src/app/components/misc-pages/misc-pages.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { NgxJsonViewerModule } from 'ngx-json-viewer';
import { AceEditorModule } from 'ng2-ace-editor';
import { SmartCommonModule } from '../common/smart-common.module';
import { FormsModule } from '@angular/forms';
import { GenerateCcdaComponent } from './generate-ccda/generate-ccda.component';

/**
* Modules containing all the components which does not directly make FHIR API Calls except ConformanceComponent
Expand All @@ -36,7 +37,8 @@ import { FormsModule } from '@angular/forms';
LaunchComponent,
RedirectComponent,
StateComponent,
UserProfileComponent
UserProfileComponent,
GenerateCcdaComponent
]
})
export class MiscPagesModule { }
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
Search Parameters Supported
<mat-list role="list">
<mat-list-item role="listitem" *ngFor="let param of searchParams" matTooltip="{{param.documentation}}">
<h4 mat-line>{{param.name}}</h4>
<p mat-line>{{param.type}}</p>
<p mat-line>Param name: {{param.name}}</p>
<p mat-line>Param type: {{param.type}}</p>
<div>{{param.documentation}}</div>
</mat-list-item>
</mat-list>
Expand All @@ -52,7 +52,7 @@ <h4 mat-line>{{param.name}}</h4>
</div>
</mat-expansion-panel>
</mat-accordion>
<app-resources-table *ngIf="!error" [bundle]="resources" [resourceType]="resourceType"></app-resources-table>
<app-resources-table *ngIf="!error" [bundle]="resources" [resourceType]="resourceType" [note]="note"></app-resources-table>
</mat-card-content>
</mat-card>
<app-object-viewer *ngIf="error" title="Error" [value]="error" hideView="true"></app-object-viewer>
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export class ResourcesTableContainerComponent implements OnInit, OnDestroy {

private _unsubscribe = new Subject<void>();
private readonly _lastUpdatedParam = '_lastUpdated';
private note: string;

constructor(
private _helperService: HelperService,
Expand Down Expand Up @@ -156,9 +157,12 @@ export class ResourcesTableContainerComponent implements OnInit, OnDestroy {
this.resources = response.data;
if (!!environment.showCCDSResourceMenuInstead && !!response.data && !!response.data.total)
{
let responseDataCopy = this._helperService.clone(response.data)
let responseDataCopy = this._helperService.clone(response.data);
// filter the results
responseDataCopy.entry = responseDataCopy.entry.filter(this.ccdsResourceType.SearchSetFilter);
if (this.ccdsResourceType.SearchSetFilter != null) {
this.note = this.ccdsResourceType.SearchSetFilter.FilterNote;
responseDataCopy.entry = responseDataCopy.entry.filter(this.ccdsResourceType.SearchSetFilter.Filter);
}
this.resources = responseDataCopy;
}
this.error = null;
Expand Down Expand Up @@ -222,12 +226,12 @@ export class ResourcesTableContainerComponent implements OnInit, OnDestroy {
if (!this.useSpecificDateParam && !!startDate)
{
if (!dateParams) dateParams = {};
dateParams['$ge'] = startDate;
dateParams['$gt'] = startDate;
}
if (!this.useSpecificDateParam && !!endDate)
{
if (!dateParams) dateParams = {};
dateParams['$le'] = endDate;
dateParams['$lt'] = endDate;
}

if (!!dateParams)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<mat-list *ngIf="bundle">
<div>Total Number of resources on the Server:{{bundle.total}}</div>
<div *ngIf="bundle.entry">Number of resources returned by the Query:{{bundle.entry.length}}</div>
<div *ngIf="isCCDSResourceMenuEnabled">The result set has been filtered to show items relevant to the selected CCDS type.</div>
<div *ngIf="note" [innerHTML]="note"></div>
<div *ngFor="let entry of bundle.entry">
<div *ngIf="entry.resource.resourceType !== 'OperationOutcome'">
<mat-list-item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ export class ResourcesTableComponent implements OnInit {
*/
@Input() resourceType: string;

/**
* Note to display
*/
@Input() note: string;


/**
* variable to hold the environment.showCCDSResourceMenuInstead value.
*/
isCCDSResourceMenuEnabled: boolean
isCCDSResourceMenuEnabled: boolean;

constructor() { }

Expand Down
Loading