Merge pull request #4 from fastenhealth/ci_test

This commit is contained in:
Jason Kulatunga 2022-09-27 07:48:51 -07:00 committed by GitHub
commit e83c6f908e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 188 additions and 1050 deletions

69
.github/workflows/build.yaml vendored Normal file
View File

@ -0,0 +1,69 @@
name: Docker
on:
push:
branches: [ main, sandbox ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
omnibus:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
# - name: "Populate frontend version information"
# run: "cd webapp/frontend && ./git.version.sh"
# - name: "Generate frontend & version information"
# uses: addnab/docker-run-action@v3
# with:
# image: node:lts
# options: -v ${{ github.workspace }}:/work
# run: |
# cd /work
# make binary-frontend && echo "print contents of /work/dist" && ls -alt /work/dist
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: 'arm64,arm'
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
tags: |
type=ref,enable=true,event=branch
type=ref,enable=true,event=tag
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
platforms: linux/amd64,linux/arm64
context: .
file: docker/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# cache-from: type=gha
# cache-to: type=gha,mode=max

View File

@ -3,5 +3,5 @@
cd frontend
npm run dist
go mod vendor
go run backend/cmd/fasten/fasten.go start --config ./config.yaml --debug
go run backend/cmd/fasten/fasten.go start --config ./config.example.yaml --debug
```

View File

@ -4,7 +4,7 @@ WORKDIR /usr/src/fastenhealth/frontend
COPY frontend/package.json ./
RUN yarn install --frozen-lockfile
COPY frontend/ ./
RUN yarn run build -- --output-path=../dist
RUN yarn run build -- --configuration sandbox --output-path=../dist
FROM golang:1.18 as backend-build
WORKDIR /go/src/github.com/fastenhealth/fastenhealth-onprem
@ -15,13 +15,20 @@ RUN go mod vendor \
&& go generate ./... \
&& go vet ./... \
&& go test ./...
RUN CGO_ENABLED=0 go build -o /go/bin/fasten ./backend/cmd/fasten/
FROM gcr.io/distroless/static-debian11
# create folder structure
RUN mkdir -p /opt/fasten/db \
mkdir -p /opt/fasten/web \
mkdir -p /opt/fasten/config
COPY --from=frontend-build /usr/src/fastenhealth/dist /opt/fasten/dist
FROM gcr.io/distroless/static-debian11
WORKDIR /opt/fasten/
COPY --from=backend-build /opt/fasten/ /opt/fasten/
COPY --from=frontend-build /usr/src/fastenhealth/dist /opt/fasten/web
COPY --from=backend-build /go/bin/fasten /opt/fasten/fasten
COPY LICENSE.md /opt/fasten/LICENSE.md
COPY config.yaml /opt/fasten/config.yaml
CMD ["/opt/fasten/fasten"]
COPY config.yaml /opt/fasten/config/config.yaml
CMD ["/opt/fasten/fasten", "start", "--config", "/opt/fasten/config/config.yaml"]

View File

@ -1,5 +1,4 @@
# goweb-template
# Fasten - On Premise/Self-Hosted
Find & replace the following

View File

@ -1,48 +0,0 @@
package cerner
import (
"context"
mock_config "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config/mock"
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
"github.com/golang/mock/gomock"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"io/ioutil"
"os"
"testing"
)
func TestCernerClient_SyncAll(t *testing.T) {
t.Parallel()
//setup
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
fakeConfig := mock_config.NewMockInterface(mockCtrl)
testDatabase, err := ioutil.TempFile("testdata", "fasten.db")
require.NoError(t, err)
defer os.Remove(testDatabase.Name())
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(testDatabase.Name())
testLogger := logrus.WithFields(logrus.Fields{
"type": "test",
})
httpClient := base.OAuthVcrSetup(t, false)
client, _, err := NewClient(context.Background(), fakeConfig, testLogger, models.Source{
SourceType: "cerner",
PatientId: "12724066",
ApiEndpointBaseUrl: "https://fhir-myrecord.cerner.com/r4/ec2458f2-1e24-41c8-b71b-0e701af7583d",
ClientId: "89efc22c-e879-4c02-a423-c3b98a0117a3",
}, httpClient)
db, err := database.NewRepository(fakeConfig, testLogger)
require.NoError(t, err)
//test
err = client.SyncAll(db)
require.NoError(t, err)
//assert
require.NoError(t, err)
}

View File

@ -21,7 +21,7 @@ func TestLogicaClient_SyncAll(t *testing.T) {
defer mockCtrl.Finish()
fakeConfig := mock_config.NewMockInterface(mockCtrl)
testDatabase, err := ioutil.TempFile("testdata", "fasten.db")
testDatabase, err := ioutil.TempFile("", "fasten.db")
require.NoError(t, err)
defer os.Remove(testDatabase.Name())
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(testDatabase.Name())

File diff suppressed because one or more lines are too long

View File

@ -10,7 +10,7 @@ web:
# used to encrypt/validate JWT session key (used for authentication)
encryptionkey: 'changethissupersecretkey'
listen:
port: 9090
port: 8080
host: 0.0.0.0
# if you're using a reverse proxy like apache/nginx, you can override this value to serve fasten on a subpath.
@ -21,8 +21,7 @@ web:
src:
# the location on the filesystem where webapp javascript + css is located
frontend:
# path: /opt/fasten/web
path: ./dist
path: /opt/fasten/web
log:
file: '' #absolute or relative paths allowed, eg. web.log

View File

@ -47,7 +47,33 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
},
"sandbox": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.sandbox.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
@ -65,6 +91,7 @@
}
]
}
}
},
"serve": {

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import {Router} from '@angular/router';
import {NavigationEnd, Router} from '@angular/router';
@Component({
selector: 'app-root',
@ -10,7 +10,7 @@ export class AppComponent implements OnInit {
title = 'fastenhealth';
public layoutOption: string;
showHeader:boolean = true;
showHeader:boolean = false;
showFooter:boolean = true;
constructor(private router: Router) {}
@ -28,8 +28,8 @@ export class AppComponent implements OnInit {
this.router.events.subscribe(event => this.modifyHeader(event));
}
modifyHeader(location) {
if(location.url?.startsWith('/auth'))
modifyHeader(event) {
if(event instanceof NavigationEnd && event.url?.startsWith('/auth'))
{
this.showHeader = false;
} else {

View File

@ -1,7 +1,6 @@
import {Component} from '@angular/core';
import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component';
import {attributeXTime} from './utils';
import {getPath} from '../../fhir/utils';
@Component({
selector: 'app-list-care-plan',
@ -13,8 +12,8 @@ export class ListCarePlanComponent extends ListGenericResourceComponent {
{ title: 'Category', versions: '*', format: 'code', getter: c => c.category[0].coding[0] },
{ title: 'Reason', versions: '*', getter: c => {
return (c.activity || []).map((a, i) => {
let reason = getPath(a, "detail.code.coding.0.display") || ""
return reason ? [reason, getPath(a, "detail.status") || "no data"] : []
let reason = a.detail?.code?.coding[0]?.display || ""
return reason ? [reason, a.detail?.status || "no data"] : []
})
} },
{ title: 'Period', versions: '*', format: 'period', getter: c => c.period },

View File

@ -1,10 +0,0 @@
// from https://raw.githubusercontent.com/smart-on-fhir/patient-browser/master/src/lib/constants.js
export const CODE_SYSTEMS = {
'LOINC' : { url: 'http://loinc.org' },
'SNOMED-CT' : { url: 'http://snomed.info/sct' },
'RxNorm' : { url: 'http://www.nlm.nih.gov/research/umls/rxnorm' },
'CVX' : { url: 'http://hl7.org/fhir/sid/cvx' },
'NUBC' : { url: 'http://www.nubc.org/patient-discharge' },
'UMLS' : { url: 'http://uts.nlm.nih.gov/metathesaurus' }
};

View File

@ -1,843 +0,0 @@
// import moment from "moment"
// import { CODE_SYSTEMS } from "./constants"
// import { intVal, getPath, parseQueryString, request } from "./utils"
//
// /**
// * This is just a helper class that is used as a query builder. It has some
// * dedicated setter methods for various query parameters and knows how to
// * compile those into a query string that can be passed to the Patient endpoint
// * of a fhir api server.
// */
// export default class PatientSearch
// {
// /**
// * The constructor just creates an empty instance. Use the setter methods
// * to set query params and then call compile to build the query string.
// */
// constructor(options = {}) {
//
// this.__cache__ = {};
//
// this.__scheduled__ = {};
//
// /**
// * The list of conditions that should be included in the query.
// * @type {Object} A map of unique keys and condition objects
// * @private
// */
// this.conditions = { ...(options.conditions || {}) };
//
// /**
// * The desired minimal age of the patients as an object like
// * { value: 5, units: "years" }
// * @type {Object}
// * @private
// */
// this.minAge = null;
//
// /**
// * The desired maximal age of the patients as an object like
// * { value: 5, units: "years" }
// * @type {Object}
// * @private
// */
// this.maxAge = null;
//
// /**
// * The patient gender to search for (male|female)
// * @type {String}
// * @private
// */
// this.gender = options.gender || null;
//
// /**
// * How many patients to fetch per page. Defaults to null meaning that
// * this param will not be included in the query and we are leaving it
// * for the server to decide.
// * @type {Number}
// * @private
// */
// this.limit = options.limit || null;
//
// /**
// * How many patients to skip. Defaults to null meaning that
// * this param will not be included in the query and we are leaving it
// * for the server to decide.
// * @type {Number}
// * @private
// */
// this.offset = null;
//
// /**
// * A collection of additional parameters
// * @type {Object}
// * @private
// */
// this.params = {};
//
// /**
// * This is like a flag that toggle the instance to different modes
// * (currently only advanced and default are supported)
// * @type {String} "advanced" or "default"
// * @private
// */
// this.queryType = options.queryType || "default";
//
// /**
// * The query string to use if in advanced mode
// * @type {String}
// * @private
// */
// this.queryString = options.queryString || "";
//
// /**
// * The sort parameters list
// * @type {String}
// * @private
// */
// this.sort = options.sort || "";
//
// /**
// * All the tags that the patients should be filtered by
// * @type {Array<String>}
// * @private
// */
// this.tags = String(options.tags || "").split(/\s*,\s*/).filter(Boolean);
// }
//
// /**
// * Schedule a prop change to be made before the next compile
// * @param {Object} props
// */
// schedule(props) {
// this.__scheduled__ = { ...this.__scheduled__, ...props };
// }
//
// hasParam(name) {
// return this.params.hasOwnProperty(name);
// }
//
// /**
// * Sets a param by name. Note that this is a lower level interface. It does
// * not know anything about the parameter thus it will not handle UI
// * dependencies (eg. it won't reset the offset for you)
// * @param {Name} name The name of the parameter to set
// * @param {*} value The value to set. Use undefined to remove a parameter
// * @returns {PatientSearch} Returns the instance
// */
// setParam(name, value) {
// let has = this.hasParam(name)
// if (value === undefined) {
// if (has) {
// delete this.params[name]
// }
// }
// else {
// this.params[name] = value
// }
// this.offset = null;
// this.cacheId = null;
// return this
// }
//
// /**
// * Sets the query type. In advanced mode a query string is provided and
// * parsed and all the other parameters are ignored. In default mode the
// * query string is ignored and only the other params are used.
// * @param {String} type "advanced" or anything else for "default"
// * @returns {PatientSearch} Returns the instance
// */
// setQueryType(type) {
// this.queryType = type == "advanced" ? "advanced" : "default"
// return this
// }
//
// /**
// * Sets the query string to be used while in advanced mode. Note that this
// * will not be used if not in advanced mode but the query string will still
// * be persisted so that if the user switches the UI to advanced the last
// * query can be displayed...
// * @param {String} query The query string to use if in advanced mode
// * @returns {PatientSearch} Returns the instance
// */
// setQueryString(query) {
// this.queryString = String(query || "")
// return this
// }
//
// /**
// * Adds a condition to the list of patient conditions
// * @param {String} key Unique string identifier for that condition
// * @param {Object} condition The condition to add
// * @returns {PatientSearch} Returns the instance
// */
// addCondition(key, condition) {
// this.conditions[key] = condition;
// this.__cache__.patientIDs = null;
// return this;
// }
//
// /**
// * Removes the condition identified by it's key. If that condition is not
// * currently included it does nothing
// * @param {*} key Unique string identifier for that condition
// * @returns {PatientSearch} Returns the instance
// */
// removeCondition(key) {
// if (this.conditions.hasOwnProperty(key)) {
// delete this.conditions[key];
// this.__cache__.patientIDs = null;
// }
// return this;
// }
//
// /**
// * Replaces the entire set of conditions at once
// * @param {Object} conditions The new conditions to set
// * @returns {PatientSearch} Returns the instance
// */
// setConditions(conditions) {
// this.conditions = { ...conditions };
// this.schedule({
// offset: null,
// cacheId: null
// });
// this.__cache__.patientIDs = null;
// return this;
// }
//
// addTag(tag) {
// if (this.tags.findIndex(tag) == -1) {
// this.tags.push(tag);
// }
// return this;
// }
//
// removeTag(tag) {
// let index = this.tags.findIndex(tag);
// if (index > -1) {
// this.tags.splice(index, 1);
// }
// return this;
// }
//
// setTags(tags) {
// this.tags = [ ...tags ]
// this.schedule({
// offset: null,
// cacheId: null
// });
// return this;
// }
//
// /**
// * Sets the desired min age af the patients. This can also be set to null
// * (or other falsy value) to exclude the minAge restrictions from the query.
// * @param {object} age The age
// * @param {number} age.value The age as number of units
// * @param {string} age.units The units for the value (years|months|days)
// * @returns {PatientSearch} Returns the instance
// */
// setMinAge(age) {
// this.minAge = age;
// this.schedule({
// offset: null,
// cacheId: null
// });
// return this;
// }
//
// /**
// * Sets the desired max age af the patients. This can also be set to null
// * (or other falsy value) to exclude the maxAge restrictions from the query.
// * @param {object} age The age
// * @param {number} age.value The age as number of units
// * @param {string} age.units The units for the value (years|months|days)
// * @returns {PatientSearch} Returns the instance
// */
// setMaxAge(age) {
// this.maxAge = age;
// this.schedule({
// offset: null,
// cacheId: null
// });
// return this;
// }
//
// /**
// * Sets the min and max ages depending on the specified age group keyword
// * @param {*} group Can be one of infant, child, adult, elderly.
// * Anything else will clear the age constraints!
// * @returns {PatientSearch} Returns the instance
// */
// setAgeGroup(group) {
// this.ageGroup = group;
// this.schedule({
// offset: null,
// cacheId: null
// });
//
// switch (group) {
//
// // infant - 0 to 12 months
// case "infant":
// this.setMinAge(null);
// this.setMaxAge({ value: 1, units: "years" });
// break;
//
// // child - 1 to 18 years
// case "child":
// this.setMinAge({ value: 1 , units: "years" });
// this.setMaxAge({ value: 18, units: "years" });
// break;
//
// // adult - 18 to 65 years
// case "adult":
// this.setMinAge({ value: 18, units: "years" });
// this.setMaxAge({ value: 65, units: "years" });
// break;
//
// // Elderly - 65+
// case "elderly":
// this.setMinAge({ value: 65, units: "years" });
// this.setMaxAge(null);
// break;
//
// // Anything else clears the birthdate param
// default:
// this.setMinAge(null);
// this.setMaxAge(null);
// // this.ageGroup = null;
// break;
// }
// return this;
// }
//
// /**
// * Sets the gender to search for. Can be "male" or "female". Any falsy value
// * will clear the gender param
// * @param {String} gender "male" or "female"
// * @returns {PatientSearch} Returns the instance
// */
// setGender(gender) {
// if (gender !== this.gender) {
// this.gender = gender;
// this.schedule({
// offset : null,
// cacheId: null
// });
// }
// return this;
// }
//
// /**
// * Sets how many patients will be fetched per page
// * @param {number|string} limit The number of records to fetch
// * @returns {PatientSearch} Returns the instance
// */
// setLimit(limit) {
// this.limit = intVal(limit)
// if (this.limit < 1) {
// this.limit = null;
// }
// return this;
// }
//
// /**
// * Sets how many patients will be skipped
// * @param {string} cacheId The id generated by the server (_getpages)
// * @param {number|string} offset The number of records to skip
// * @returns {PatientSearch} Returns the instance
// */
// setOffset(cacheId, offset) {
// this.offset = intVal(offset)
// this.cacheId = cacheId
// if (this.offset < 1) {
// this.offset = null;
// this.cacheId = null;
// }
// return this;
// }
//
// /**
// * Sets the sorting to use
// * @param {string} sort A fhir sort string like "status,-date,category"
// * @returns {PatientSearch} Returns the instance
// */
// setSort(sort) {
// this.sort = sort;
// this.offset = null;
// this.cacheId = null;
// return this
// }
//
// /**
// * Returns another PatientSearch instance with the exact same state as this.
// * @returns {PatientSearch} Returns the new copy
// */
// clone() {
// let inst = new PatientSearch();
//
// inst.conditions = { ...this.conditions };
// inst.params = { ...this.params };
// inst.tags = [ ...this.tags ];
//
// inst.setSort(this.sort)
// .setAgeGroup(this.ageGroup)
// .setMinAge(this.minAge)
// .setMaxAge(this.maxAge)
// .setGender(this.gender)
// .setLimit(this.limit)
// .setOffset(this.cacheId, this.offset)
// .setQueryType(this.queryType)
// .setQueryString(this.queryString);
//
// return inst;
// }
//
// /**
// * Clear all params. If you call compile after clear only the "_format=json"
// * part should be returned
// * @returns {PatientSearch} Returns the instance
// */
// reset() {
// this.conditions = {};
// this.minAge = null;
// this.maxAge = null;
// this.gender = null;
// this.limit = null;
// this.offset = null;
// this.cacheId = null;
// this.ageGroup = null;
// this.params = {};
// this.queryString = "";
// this.queryType = "default"
// this.sort = "";
// this.tags = [];
// return this;
// }
//
// /**
// * Returns an object representing the current state of the instance.
// * The object contains COPIES of the current param values.
// * @returns {Object}
// */
// getState() {
// return {
// conditions : this.conditions,
// minAge : this.minAge,
// maxAge : this.maxAge,
// gender : this.gender,
// limit : this.limit,
// offset : this.offset,
// cacheId : this.cacheId,
// ageGroup : this.ageGroup,
// params : { ...this.params },
// queryString : this.queryString,
// queryType : this.queryType,
// sort : this.sort,
// tags : [ ...this.tags ]
// };
// }
//
// /**
// * Compiles and returns the query string that can be send to the Patient
// * endpoint.
// * @return {String} The compiled query string (without the "?" in front)
// */
// compile(encode=true) {
// let params = [];
//
// [
// // conditions
// "minAge",
// "maxAge",
// "gender",
// "limit",
// "offset",
// // "params",
// "queryType",
// "queryString",
// "sort"//,
// // tags
// ].forEach(prop => {
// if (this.__scheduled__.hasOwnProperty(prop)) {
// this[prop] = this.__scheduled__[prop];
// delete this.__scheduled__[prop];
// }
// })
//
// // Tags ----------------------------------------------------------------
// if (this.tags.length && !this.params._id) {
// params.push({ name: "_tag", value: this.tags.join(",") });
// }
//
// // Advanced query ------------------------------------------------------
// if (this.queryType == "advanced") {
// let str = this.queryString.trim()
// if (str) {
// let _query = parseQueryString(str);
// for (let name in _query) {
// params.push({ name, value: _query[name] });
// }
// }
// }
//
// // Default query -------------------------------------------------------
// else {
//
// // Custom params ---------------------------------------------------
// Object.keys(this.params).forEach(k => {
// if (String(this.params[k]).trim()) {
// params.push({
// name : k,
// value: this.params[k]
// });
// }
// });
//
// // sort ------------------------------------------------------------
// if (this.sort) {
// String(this.sort).split(",").forEach(token => {
// if (token.indexOf("-") === 0) {
// params.push({
// name : "_sort:desc",
// value: token.substring(1)
// })
// }
// else {
// params.push({
// name : "_sort:asc",
// value: token
// })
// }
// })
// // params.push({
// // name : "_sort",
// // value: this.sort
// // })
// }
//
// if (!this.params._id) {
//
// // Min age -----------------------------------------------------
// if (this.minAge) {
// let d = moment().subtract(
// this.minAge.value,
// this.minAge.units
// );
// params.push({
// name : "birthdate",
// value: "le" + d.format('YYYY-MM-DD')
// });
// }
//
// // Max age -----------------------------------------------------
// if (this.maxAge) {
// let d = moment().subtract(
// this.maxAge.value,
// this.maxAge.units
// );
// params.push({
// name : "birthdate",
// value: "ge" + d.format('YYYY-MM-DD')
// });
// }
//
// // exclude deceased patients if age is specified ---------------
// if (this.maxAge || this.minAge) {
// let existing = params.find(p => p.name === "deceased");
// if (existing) {
// existing.value = false;
// }
// else {
// params.push({
// name : "deceased",
// value: false
// });
// }
// }
//
// // Gender ------------------------------------------------------
// if (this.gender) {
// params.push({
// name : "gender",
// value: this.gender
// });
// }
// }
// }
//
// // limit ---------------------------------------------------------------
// if (this.limit) {
// params.push({
// name : "_count",
// value: this.limit
// });
// }
//
// // offset --------------------------------------------------------------
// if (this.offset && this.cacheId) {
// params.push({
// name: "_getpages",
// value: this.cacheId
// }, {
// name : "_getpagesoffset",
// value: this.offset
// });
// }
//
// // Compile and return --------------------------------------------------
// return params.map(p => (
// encode ?
// encodeURIComponent(p.name) + "=" + encodeURIComponent(p.value) :
// p.name + "=" + p.value
// )).join("&");
// }
//
// /**
// * Checks if there are any conditions chosen at the moment
// * @returns {Boolean}
// */
// hasConditions() {
// for (let key in this.conditions) {
// if (this.conditions.hasOwnProperty(key)) {
// return true;
// }
// }
// return false;
// }
//
// /**
// * Compiles the current conditions into URL-encoded parameter list
// * @returns {String}
// */
// compileConditions() {
// // let params = [];
//
// // for (let key in this.conditions) {
// // let condition = this.conditions[key]
//
// // // system
// // let value = [];
// // for (let system in condition.codes) {
// // let systemUrl = (CODE_SYSTEMS[system] || {}).url;
//
// // // system.code[n] - OR
// // condition.codes[system].forEach(c => {
// // value.push( systemUrl ? systemUrl + "|" + c : c );
// // })
// // }
//
// // if (value.length) {
// // params.push({
// // name : "code",
// // value: value.join(",")
// // })
// // }
// // }
//
// // return params.map(p => (
// // encodeURIComponent(p.name) + "=" + encodeURIComponent(p.value)
// // )).join("&");
// let out = []
// for (let key in this.conditions) {
// let condition = this.conditions[key]
//
// // system
// let value = [];
// for (let system in condition.codes) {
// let systemUrl = (CODE_SYSTEMS[system] || {}).url || "http://snomed.info/sct";
//
// // system.code[n] - OR
// condition.codes[system].forEach(c => {
// value.push(systemUrl + "|" + c);
// })
// }
//
// if (value.length) {
// out.push(value.join(","));
// }
// }
// return out.length ? "code=" + encodeURIComponent(out.join(",")) : "";
// }
//
// getConditionKeys() {
// let out = []
// for (let key in this.conditions) {
// let condition = this.conditions[key]
//
// // system
// let value = [];
// for (let system in condition.codes) {
// let systemUrl = (CODE_SYSTEMS[system] || {}).url || "http://snomed.info/sct";
//
// // system.code[n] - OR
// condition.codes[system].forEach(c => {
// value.push(systemUrl + "|" + c);
// })
// }
//
// if (value.length) {
// out.push(value.join(","));
// }
// }
// return out;
// }
//
// /**
// * Returns a promise resolved with a list of patient IDs that have the
// * specified condition(s)
// * @param {String} baseURL
// * @returns {Promise<String[]>}
// */
// getPatientIDs(server) {
//
// if (this.__cache__.patientIDs) {
// return Promise.resolve(this.__cache__.patientIDs);
// }
//
// let conditions = this.compileConditions();
//
// if (!conditions) {
// return Promise.resolve([]);
// }
//
// /**
// * The keys (eg: "http://snomed.info/sct|44054006") that were set by the
// * user.
// * @type {Array<String>}
// * @private
// */
// let conditionKeys = this.getConditionKeys();
//
// /**
// * Map of patient IDs as keys and array of condition keys as values.
// * @private
// */
// let patientIDs = {};
//
// /**
// * Handles the JSON response (single page) of the conditions query.
// * Collects the patient IDs and their condition codes into the
// * patientIDs local variable. When all the pages are fetched, cleans up
// * the IDs to only contain those that have all the conditions specified
// * by the user.
// * @param {Object} response The JSON Conditions bundle response
// * @returns {Promise<any>} Array of patient ID strings (can be empty)
// */
// const handleConditionsResponse = response => {
//
// // Collect the data
// if (response.entry) {
// response.entry.forEach(condition => {
// let patientID = server.type == "DSTU-2" ?
// condition.resource.patient.reference.split("/").pop():
// condition.resource.subject.reference.split("/").pop();
// if (!patientIDs[patientID]) {
// patientIDs[patientID] = [];
// }
// patientIDs[patientID].push(
// (getPath(condition, "resource.code.coding.0.system") || "http://snomed.info/sct")
// + "|" + getPath(condition, "resource.code.coding.0.code")
// );
// });
//
// let nextLink = (response.link || []).find(l => l.relation == "next");
// if (nextLink) {
// return request({ url: nextLink.url }).then(handleConditionsResponse);
// }
// }
// // console.log(conditionKeys, patientIDs)
// // Clean up and only leave patients having all the conditions
// patientIDs = Object.keys(patientIDs).filter(key => {
// return conditionKeys.every(
// conditionKey => patientIDs[key].indexOf(conditionKey) > -1
// );
// });
// // console.log(patientIDs)
//
// // finally return a promise resolved with the compiled ID array
// return Promise.resolve(patientIDs);
// }
//
// // The conditions to search for
// let params = [conditions];
//
// // only need the patient - skip the rest to reduce the response
// params.push(
// server.type == "DSTU-2" ?
// "_elements=patient,code" :
// "_elements=subject,code"
// );
//
// // Set bigger limit here to reduce the chance of having to
// // make other queries to fetch subsequent pages
// params.push("_count=500");
//
// // Tags (not currently available in STU2)
// if (this.tags.length) {
// params.push( "_tag=" + encodeURIComponent(this.tags.join(",")) );
// }
//
// return request({
// url: `${server.url}/Condition?${params.join("&")}`
// })
// .then(handleConditionsResponse)
// .then(ids => {
// this.__cache__.patientIDs = ids;
// return ids;
// });
// }
//
// /**
// * Fetches the patients matching the user-defined conditions. The actual
// * strategy may vary but regardless of the implementation, a promise is
// * returned that should eventually be resolved with the result bundle.
// * @param {String} baseURL
// * @returns {Promise<Bundle>}
// */
// fetch(server) {
//
// let data = this.compile()
//
// // STU2 does not work with the deceased param
// if (server.type == "DSTU-2") {
// data = data.replace(/\bdeceased=(true|false)\b/gi, "");
// }
//
// // prepare the base options for the patient ajax request
// let options = {
// url: this.offset && this.cacheId ? server.url : `${server.url}/Patient/_search`,
// method: this.offset && this.cacheId ? "GET" : "POST",
// processData: false,
// data,
// headers: {
// accept: "application/json+fhir",
// "content-type": "application/x-www-form-urlencoded"
// }
// };
//
// return this.getPatientIDs(server)
// .then(ids => {
// if (ids.length) {
// // if IDs were found - add them to the patient query
// options.data = [
// options.data,
// "_id=" + encodeURIComponent(ids.join(","))
// ].filter(Boolean).join("&");
// }
// else {
// // If conditions were specified but no patients were found to
// // have those conditions, then we should exit early.
// if (this.hasConditions()) {
// return Promise.reject(
// "No patients found with the specified conditions!"
// );
// }
// }
// return options;
// })
// .then(request);
// }
// }

View File

@ -1,21 +0,0 @@
import {getPath} from '../../fhir/utils';
export class CarePlan {
category: string
reason: string[][]
periodStart: string
periodEnd: string
status: string
constructor(resourcePayload: any) {
this.category = getPath(resourcePayload, "category.0.coding.0.display")
this.reason = (resourcePayload.activity || []).map((a, i) => {
let reason = getPath(a, "detail.code.coding.0.display") || ""
return reason ? [reason, getPath(a, "detail.status") || "no data"] : []
}).filter((arr) => {return arr.length > 0 })
this.periodStart = resourcePayload.period.start
this.periodEnd = resourcePayload.period.end
this.status = resourcePayload.status
}
}

View File

@ -1,21 +0,0 @@
import {getPath} from '../../fhir/utils';
export class Encounter {
encounterType: string
encounterClass: string
reason: string
status: string
constructor(resourcePayload: any) {
this.encounterType = getPath(resourcePayload, "type.0.text");
this.encounterClass = this.getEncounterClass(resourcePayload);
this.reason = getPath(resourcePayload, "reason.0.coding.0.display")
this.status = getPath(resourcePayload, "status")
}
getEncounterClass(encounter) {
return encounter.class && typeof encounter.class == "object" ?
getPath(encounter, "class.type.0.text") :
encounter.class;
}
}

View File

@ -1,12 +0,0 @@
import * as moment from "moment"
export class Period {
from: moment.Moment
to: moment.Moment
constructor(startDate?: string, endDate?: string) {
this.from = startDate ? moment(startDate) : null
this.to = endDate ? moment(endDate) : null
}
}

View File

@ -0,0 +1,17 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: true,
lighthouse_api_endpoint_base: 'https://lighthouse.fastenhealth.com/sandbox'
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.