adding support for dropzone
adding support for manual source
This commit is contained in:
parent
96a7a394ab
commit
5f71891970
|
@ -5,6 +5,8 @@ package pkg
|
|||
type SourceType string
|
||||
|
||||
const (
|
||||
SourceTypeManual SourceType = "manual"
|
||||
|
||||
SourceTypeAetna SourceType = "aetna"
|
||||
SourceTypeAnthem SourceType = "anthem"
|
||||
SourceTypeCigna SourceType = "cigna"
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/aetna"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/cigna"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/manual"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
@ -26,6 +27,8 @@ func NewClient(sourceType pkg.SourceType, ctx context.Context, appConfig config.
|
|||
sourceClient, updatedSource, err = cigna.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeCigna:
|
||||
sourceClient, updatedSource, err = cigna.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
case pkg.SourceTypeManual:
|
||||
sourceClient, updatedSource, err = manual.NewClient(ctx, appConfig, globalLogger, credentials, testHttpClient...)
|
||||
default:
|
||||
return nil, updatedSource, errors.New(fmt.Sprintf("Unknown Source Type: %s", sourceType))
|
||||
}
|
||||
|
|
|
@ -5,10 +5,13 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -22,6 +25,10 @@ type BaseClient struct {
|
|||
Source models.Source
|
||||
}
|
||||
|
||||
func (c *BaseClient) SyncAllBundle(db database.DatabaseRepository, bundleFile *os.File) error {
|
||||
panic("SyncAllBundle functionality is not available on this client")
|
||||
}
|
||||
|
||||
func NewBaseClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*BaseClient, *models.Source, error) {
|
||||
var httpClient *http.Client
|
||||
var updatedSource *models.Source
|
||||
|
@ -76,6 +83,7 @@ func NewBaseClient(ctx context.Context, appConfig config.Interface, globalLogger
|
|||
}
|
||||
|
||||
httpClient.Timeout = 10 * time.Second
|
||||
|
||||
return &BaseClient{
|
||||
Context: ctx,
|
||||
AppConfig: appConfig,
|
||||
|
@ -101,13 +109,19 @@ func (c *BaseClient) GetRequest(resourceSubpath string, decodeModelPtr interface
|
|||
return fmt.Errorf("An error occurred during request %s - %d - %s", url, resp.StatusCode, resp.Status)
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(decodeModelPtr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ParseBundle(resp.Body, decodeModelPtr)
|
||||
return err
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper Functions
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func ParseBundle(r io.Reader, decodeModelPtr interface{}) error {
|
||||
decoder := json.NewDecoder(r)
|
||||
//decoder.DisallowUnknownFields() //make sure we throw an error if unknown fields are present.
|
||||
err := decoder.Decode(decodeModelPtr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -80,63 +80,3 @@ func (c *FHIR401Client) ProcessBundle(bundle fhir401.Bundle) ([]models.ResourceF
|
|||
})
|
||||
return wrappedResourceModels, nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Process & Generate API/Database Models
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//func (c *FHIR401Client) ProcessPatient(item fhir401.Patient) (models.Profile, error) {
|
||||
// c.Logger.Debugf("item %v", item)
|
||||
// patientProfile := models.Profile{
|
||||
// OriginBase: models.OriginBase{
|
||||
// ModelBase: models.ModelBase{},
|
||||
// UserID: c.Source.UserID,
|
||||
// SourceID: c.Source.ID,
|
||||
// SourceResourceID: *item.Id,
|
||||
// SourceResourceType: fhir401.ResourceTypePatient.Code(),
|
||||
// },
|
||||
// Demographics: models.Demographics{
|
||||
// Address: models.Address{},
|
||||
// Name: models.Name{},
|
||||
// },
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// if item.Address != nil && len(item.Address) > 0 {
|
||||
// itemAddress := item.Address[0]
|
||||
// patientProfile.Demographics.Address.City = itemAddress.City
|
||||
// patientProfile.Demographics.Address.Country = itemAddress.Country
|
||||
// patientProfile.Demographics.Address.State = itemAddress.State
|
||||
// patientProfile.Demographics.Address.Street = itemAddress.Line
|
||||
// patientProfile.Demographics.Address.Zip = itemAddress.PostalCode
|
||||
//
|
||||
// }
|
||||
// patientProfile.Demographics.Dob = item.BirthDate
|
||||
//
|
||||
// if item.Gender != nil {
|
||||
// itemGenderStr := item.Gender.String()
|
||||
// itemGenderCode := item.Gender.Code()
|
||||
// patientProfile.Demographics.Gender = &itemGenderStr
|
||||
// patientProfile.Demographics.GenderCodes = &itemGenderCode
|
||||
// }
|
||||
// patientProfile.Demographics.Language = item.Language
|
||||
//
|
||||
// if item.MaritalStatus != nil {
|
||||
// patientProfile.Demographics.MaritalStatus = item.MaritalStatus.Text
|
||||
// if len(item.MaritalStatus.Coding) > 0 {
|
||||
// patientProfile.Demographics.MaritalStatusCodes = item.MaritalStatus.Coding[0].Code
|
||||
// }
|
||||
// }
|
||||
// if item.Name != nil && len(item.Name) > 0 {
|
||||
// itemName := item.Name[0]
|
||||
// if itemName.Prefix != nil && len(itemName.Prefix) > 0 {
|
||||
// itemNamePrefix := itemName.Prefix[0]
|
||||
// patientProfile.Demographics.Name.Prefix = &itemNamePrefix
|
||||
// }
|
||||
// patientProfile.Demographics.Name.Given = itemName.Given
|
||||
// patientProfile.Demographics.Name.Family = itemName.Family
|
||||
//
|
||||
// }
|
||||
//
|
||||
// return patientProfile, nil
|
||||
//}
|
||||
|
|
|
@ -6,7 +6,10 @@ import (
|
|||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/fastenhealth/gofhir-models/fhir430"
|
||||
fhirutils "github.com/fastenhealth/gofhir-models/fhir430/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/datatypes"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
@ -37,5 +40,42 @@ func (c *FHIR430Client) GetPatient(patientId string) (*fhir430.Patient, error) {
|
|||
patient := fhir430.Patient{}
|
||||
err := c.GetRequest(fmt.Sprintf("Patient/%s", patientId), &patient)
|
||||
return &patient, err
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Process Bundles
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
func (c *FHIR430Client) ProcessBundle(bundle fhir430.Bundle) ([]models.ResourceFhir, error) {
|
||||
|
||||
//process each entry in bundle
|
||||
wrappedResourceModels := lo.FilterMap[fhir430.BundleEntry, models.ResourceFhir](bundle.Entry, func(bundleEntry fhir430.BundleEntry, _ int) (models.ResourceFhir, bool) {
|
||||
originalResource, _ := fhirutils.MapToResource(bundleEntry.Resource, false)
|
||||
|
||||
resourceType, resourceId := originalResource.(ResourceInterface).ResourceRef()
|
||||
|
||||
// TODO find a way to safely/consistently get the resource updated date (and other metadata) which shoudl be added to the model.
|
||||
//if originalResource.Meta != nil && originalResource.Meta.LastUpdated != nil {
|
||||
// if parsed, err := time.Parse(time.RFC3339Nano, *originalResource.Meta.LastUpdated); err == nil {
|
||||
// patientProfile.UpdatedAt = parsed
|
||||
// }
|
||||
//}
|
||||
if resourceId == nil {
|
||||
//no resourceId present for this resource, we'll ignore it.
|
||||
return models.ResourceFhir{}, false
|
||||
}
|
||||
|
||||
wrappedResourceModel := models.ResourceFhir{
|
||||
OriginBase: models.OriginBase{
|
||||
ModelBase: models.ModelBase{},
|
||||
UserID: c.Source.UserID,
|
||||
SourceID: c.Source.ID,
|
||||
SourceResourceID: *resourceId,
|
||||
SourceResourceType: resourceType,
|
||||
},
|
||||
Payload: datatypes.JSON(bundleEntry.Resource),
|
||||
}
|
||||
|
||||
return wrappedResourceModel, true
|
||||
})
|
||||
return wrappedResourceModels, nil
|
||||
}
|
||||
|
|
|
@ -1,28 +1,17 @@
|
|||
package base
|
||||
|
||||
import "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
import (
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"os"
|
||||
)
|
||||
|
||||
//go:generate mockgen -source=interface.go -destination=mock/mock_client.go
|
||||
type Client interface {
|
||||
GetRequest(resourceSubpath string, decodeModelPtr interface{}) error
|
||||
SyncAll(db database.DatabaseRepository) error
|
||||
|
||||
//PatientProfile() (models.PatientProfile, error)
|
||||
//Allergies()
|
||||
//Encounters()
|
||||
//Immunizations()
|
||||
//Instructions()
|
||||
//Medications()
|
||||
//Narratives()
|
||||
//Organizations()
|
||||
//PlansOfCare()
|
||||
//Problems()
|
||||
//Procedures()
|
||||
//TestResults()
|
||||
//Vitals()
|
||||
//CCD()
|
||||
//Demographics()
|
||||
//SocialHistory()
|
||||
//Manual client ONLY functions
|
||||
SyncAllBundle(db database.DatabaseRepository, bundleFile *os.File) error
|
||||
}
|
||||
|
||||
type ResourceInterface interface {
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
package manual
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config"
|
||||
"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/fastenhealth/gofhir-models/fhir401"
|
||||
fhir401utils "github.com/fastenhealth/gofhir-models/fhir401/utils"
|
||||
"github.com/fastenhealth/gofhir-models/fhir430"
|
||||
fhir430utils "github.com/fastenhealth/gofhir-models/fhir430/utils"
|
||||
"github.com/samber/lo"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ManualClient struct {
|
||||
Context context.Context
|
||||
AppConfig config.Interface
|
||||
Logger logrus.FieldLogger
|
||||
|
||||
Source *models.Source
|
||||
}
|
||||
|
||||
func (m ManualClient) GetRequest(resourceSubpath string, decodeModelPtr interface{}) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m ManualClient) SyncAll(db database.DatabaseRepository) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (m ManualClient) SyncAllBundle(db database.DatabaseRepository, bundleFile *os.File) error {
|
||||
|
||||
// we need to find the (most populated) patient record
|
||||
patientId, bundleType, err := m.ExtractPatientId(bundleFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while extracting patient id from bundle: %w", err)
|
||||
}
|
||||
// we need to add the patient id to the source
|
||||
m.Source.PatientId = patientId
|
||||
|
||||
// we need to upsert Source
|
||||
err = db.CreateSource(m.Context, m.Source)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while creating manual source: %w", err)
|
||||
}
|
||||
// we need to parse the bundle into resources (might need to try a couple of different times)
|
||||
var resourceFhirList []models.ResourceFhir
|
||||
switch bundleType {
|
||||
case "fhir430":
|
||||
bundle430Data := fhir430.Bundle{}
|
||||
err := base.ParseBundle(bundleFile, &bundle430Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while parsing 4.3.0 bundle: %w", err)
|
||||
}
|
||||
client, _, err := base.NewFHIR430Client(m.Context, m.AppConfig, m.Logger, *m.Source, http.DefaultClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while creating 4.3.0 client: %w", err)
|
||||
}
|
||||
resourceFhirList, err = client.ProcessBundle(bundle430Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while processing 4.3.0 resources: %w", err)
|
||||
}
|
||||
case "fhir401":
|
||||
bundle401Data := fhir401.Bundle{}
|
||||
err := base.ParseBundle(bundleFile, &bundle401Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while parsing 4.0.1 bundle: %w", err)
|
||||
}
|
||||
client, _, err := base.NewFHIR401Client(m.Context, m.AppConfig, m.Logger, *m.Source, http.DefaultClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while creating 4.0.1 client: %w", err)
|
||||
}
|
||||
resourceFhirList, err = client.ProcessBundle(bundle401Data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while processing 4.0.1 resources: %w", err)
|
||||
}
|
||||
}
|
||||
// we need to upsert all resources (and make sure they are associated with new Source)
|
||||
for _, apiModel := range resourceFhirList {
|
||||
err = db.UpsertResource(context.Background(), apiModel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("an error occurred while upserting resources: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m ManualClient) ExtractPatientId(bundleFile *os.File) (string, string, error) {
|
||||
// try from newest format to the oldest format
|
||||
bundle430Data := fhir430.Bundle{}
|
||||
bundle401Data := fhir401.Bundle{}
|
||||
|
||||
var patientIds []string
|
||||
|
||||
bundleType := "fhir430"
|
||||
if err := base.ParseBundle(bundleFile, &bundle430Data); err == nil {
|
||||
patientIds = lo.FilterMap[fhir430.BundleEntry, string](bundle430Data.Entry, func(bundleEntry fhir430.BundleEntry, _ int) (string, bool) {
|
||||
parsedResource, err := fhir430utils.MapToResource(bundleEntry.Resource, false)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
typedResource := parsedResource.(base.ResourceInterface)
|
||||
resourceType, resourceId := typedResource.ResourceRef()
|
||||
|
||||
if resourceId == nil || len(*resourceId) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return *resourceId, resourceType == fhir430.ResourceTypePatient.String()
|
||||
})
|
||||
}
|
||||
bundleFile.Seek(0, io.SeekStart)
|
||||
|
||||
//fallback
|
||||
if patientIds == nil || len(patientIds) == 0 {
|
||||
bundleType = "fhir401"
|
||||
//try parsing the bundle as a 401 bundle
|
||||
//TODO: find a better, more generic way to do this.
|
||||
err := base.ParseBundle(bundleFile, &bundle401Data)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
patientIds = lo.FilterMap[fhir401.BundleEntry, string](bundle401Data.Entry, func(bundleEntry fhir401.BundleEntry, _ int) (string, bool) {
|
||||
parsedResource, err := fhir401utils.MapToResource(bundleEntry.Resource, false)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
typedResource := parsedResource.(base.ResourceInterface)
|
||||
resourceType, resourceId := typedResource.ResourceRef()
|
||||
|
||||
if resourceId == nil || len(*resourceId) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return *resourceId, resourceType == fhir430.ResourceTypePatient.String()
|
||||
})
|
||||
}
|
||||
bundleFile.Seek(0, io.SeekStart)
|
||||
|
||||
if patientIds == nil || len(patientIds) == 0 {
|
||||
return "", "", fmt.Errorf("could not determine patient id")
|
||||
} else {
|
||||
//reset reader
|
||||
|
||||
return strings.TrimLeft(patientIds[0], "Patient/"), bundleType, nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
|
||||
return ManualClient{
|
||||
Context: ctx,
|
||||
AppConfig: appConfig,
|
||||
Logger: globalLogger,
|
||||
Source: &models.Source{
|
||||
SourceType: pkg.SourceTypeManual,
|
||||
},
|
||||
}, nil, nil
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub"
|
||||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
@ -57,6 +59,52 @@ func CreateSource(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceCred})
|
||||
}
|
||||
|
||||
func CreateManualSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
// single file
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "could not extract file from form"})
|
||||
return
|
||||
}
|
||||
fmt.Printf("Uploaded filename: %s", file.Filename)
|
||||
|
||||
// create a temporary file to store this uploaded file
|
||||
bundleFile, err := ioutil.TempFile("", file.Filename)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "could not create temp file"})
|
||||
return
|
||||
}
|
||||
|
||||
// Upload the file to specific bundleFile.
|
||||
err = c.SaveUploadedFile(file, bundleFile.Name())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "could not save temp file"})
|
||||
return
|
||||
}
|
||||
|
||||
// We cannot save the "Source" object yet, as we do not know the patientID
|
||||
|
||||
// create a "manual" client, which we can use to parse the
|
||||
manualSourceClient, _, err := hub.NewClient(pkg.SourceTypeManual, c, nil, logger, models.Source{})
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while initializing hub client using manual source without credentials", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
||||
err = manualSourceClient.SyncAllBundle(databaseRepo, bundleFile)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while processing bundle", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": fmt.Sprintf("'%s' uploaded!", file.Filename)})
|
||||
}
|
||||
|
||||
func GetSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
|
|
@ -46,6 +46,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
|||
secure := api.Group("/secure").Use(middleware.RequireAuth())
|
||||
{
|
||||
secure.POST("/source", handler.CreateSource)
|
||||
secure.POST("/source/manual", handler.CreateManualSource)
|
||||
secure.GET("/source", handler.ListSource)
|
||||
secure.GET("/source/:sourceId", handler.GetSource)
|
||||
//in debug mode, this endpoint lets us request data directly from the source api
|
||||
|
|
|
@ -24,10 +24,14 @@
|
|||
"@fortawesome/free-solid-svg-icons": "^6.2.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "10.0.0",
|
||||
"@panva/oauth4webapi": "^1.1.3",
|
||||
"angular-datatables": "^14.0.0",
|
||||
"bootstrap": "^4.4.1",
|
||||
"chart.js": "2.9.4",
|
||||
"fhirclient": "^2.5.1",
|
||||
"humanize-duration": "^3.27.3",
|
||||
"moment": "^2.29.4",
|
||||
"ng2-charts": "^2.3.0",
|
||||
"ngx-dropzone": "^3.1.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.11.8"
|
||||
|
@ -3946,6 +3950,87 @@
|
|||
"ajv": "^6.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/angular-datatables": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-datatables/-/angular-datatables-14.0.0.tgz",
|
||||
"integrity": "sha512-IdJdS/IGAFKcWKCM3PrgHt8YIyCGxUKaPQGQom+5YvAcIMKuRwgB7aYT2H3+zUsJBNms2Yaeh95ABNT+yqOwLQ==",
|
||||
"dependencies": {
|
||||
"codelyzer": "^6.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/angular-datatables/node_modules/@angular/compiler": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz",
|
||||
"integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==",
|
||||
"peerDependencies": {
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/angular-datatables/node_modules/@angular/core": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz",
|
||||
"integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==",
|
||||
"peerDependencies": {
|
||||
"rxjs": "^6.5.3",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/angular-datatables/node_modules/app-root-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz",
|
||||
"integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==",
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/angular-datatables/node_modules/codelyzer": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.2.tgz",
|
||||
"integrity": "sha512-v3+E0Ucu2xWJMOJ2fA/q9pDT/hlxHftHGPUay1/1cTgyPV5JTHFdO9hqo837Sx2s9vKBMTt5gO+lhF95PO6J+g==",
|
||||
"dependencies": {
|
||||
"@angular/compiler": "9.0.0",
|
||||
"@angular/core": "9.0.0",
|
||||
"app-root-path": "^3.0.0",
|
||||
"aria-query": "^3.0.0",
|
||||
"axobject-query": "2.0.2",
|
||||
"css-selector-tokenizer": "^0.7.1",
|
||||
"cssauron": "^1.4.0",
|
||||
"damerau-levenshtein": "^1.0.4",
|
||||
"rxjs": "^6.5.3",
|
||||
"semver-dsl": "^1.0.1",
|
||||
"source-map": "^0.5.7",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/compiler": ">=2.3.1 <13.0.0 || ^12.0.0-next || ^12.1.0-next || ^12.2.0-next",
|
||||
"@angular/core": ">=2.3.1 <13.0.0 || ^12.0.0-next || ^12.1.0-next || ^12.2.0-next",
|
||||
"tslint": "^5.0.0 || ^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/angular-datatables/node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/angular-datatables/node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/angular-datatables/node_modules/zone.js": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz",
|
||||
"integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg=="
|
||||
},
|
||||
"node_modules/ansi-colors": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||
|
@ -4081,7 +4166,6 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
|
||||
"integrity": "sha512-majUxHgLehQTeSA+hClx+DY09OVUqG3GtezWkF1krgLGNdlDu9l9V8DaqNMWbq4Eddc8wsyDA0hpDUtnYxQEXw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ast-types-flow": "0.0.7",
|
||||
"commander": "^2.11.0"
|
||||
|
@ -4150,8 +4234,7 @@
|
|||
"node_modules/ast-types-flow": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
|
||||
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
|
||||
"dev": true
|
||||
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag=="
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
|
@ -4232,7 +4315,6 @@
|
|||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
|
||||
"integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ast-types-flow": "0.0.7"
|
||||
}
|
||||
|
@ -5052,8 +5134,7 @@
|
|||
"node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"node_modules/commondir": {
|
||||
"version": "1.0.1",
|
||||
|
@ -5640,7 +5721,6 @@
|
|||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
|
||||
"integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"fastparse": "^1.1.2"
|
||||
|
@ -5671,7 +5751,6 @@
|
|||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz",
|
||||
"integrity": "sha512-Ht70DcFBh+/ekjVrYS2PlDMdSQEl3OFNmjK6lcn49HptBgilXf/Zwg4uFh9Xn0pX3Q8YOkSjIFOfK2osvdqpBw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"through": "X.X.X"
|
||||
}
|
||||
|
@ -5690,7 +5769,6 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"cssesc": "bin/cssesc"
|
||||
},
|
||||
|
@ -5707,8 +5785,7 @@
|
|||
"node_modules/damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
||||
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
|
||||
},
|
||||
"node_modules/dashdash": {
|
||||
"version": "1.14.1",
|
||||
|
@ -7077,8 +7154,7 @@
|
|||
"node_modules/fastparse": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
||||
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ=="
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.13.0",
|
||||
|
@ -7842,6 +7918,11 @@
|
|||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/humanize-duration": {
|
||||
"version": "3.27.3",
|
||||
"resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.3.tgz",
|
||||
"integrity": "sha512-iimHkHPfIAQ8zCDQLgn08pRqSVioyWvnGfaQ8gond2wf7Jq2jJ+24ykmnRyiz3fIldcn4oUuQXpjqKLhSVR7lw=="
|
||||
},
|
||||
"node_modules/humanize-ms": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
||||
|
@ -9809,6 +9890,14 @@
|
|||
"rxjs": "^6.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-dropzone": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-dropzone/-/ngx-dropzone-3.1.0.tgz",
|
||||
"integrity": "sha512-5RBaEl07QUcY6sv/BBPyIxN6nbWY/KqTGheEKgbuGS0N1QPFY7NJUo8+X3fYUwQgLS+wjJeqPiR37dd0YNDtWA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nice-napi": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
|
||||
|
@ -12491,7 +12580,6 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz",
|
||||
"integrity": "sha512-e8BOaTo007E3dMuQQTnPdalbKTABKNS7UxoBIDnwOqRa+QwMrCPjynB8zAlPF6xlqUfdLPPLIJ13hJNmhtq8Ng==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"semver": "^5.3.0"
|
||||
}
|
||||
|
@ -12500,7 +12588,6 @@
|
|||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
|
@ -13005,8 +13092,7 @@
|
|||
"node_modules/sprintf-js": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
|
||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
|
||||
"dev": true
|
||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
|
||||
},
|
||||
"node_modules/sshpk": {
|
||||
"version": "1.17.0",
|
||||
|
@ -13333,8 +13419,7 @@
|
|||
"node_modules/through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
|
||||
},
|
||||
"node_modules/thunky": {
|
||||
"version": "1.1.0",
|
||||
|
@ -17322,6 +17407,67 @@
|
|||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"angular-datatables": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-datatables/-/angular-datatables-14.0.0.tgz",
|
||||
"integrity": "sha512-IdJdS/IGAFKcWKCM3PrgHt8YIyCGxUKaPQGQom+5YvAcIMKuRwgB7aYT2H3+zUsJBNms2Yaeh95ABNT+yqOwLQ==",
|
||||
"requires": {
|
||||
"codelyzer": "^6.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/compiler": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz",
|
||||
"integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ=="
|
||||
},
|
||||
"@angular/core": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz",
|
||||
"integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w=="
|
||||
},
|
||||
"app-root-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz",
|
||||
"integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA=="
|
||||
},
|
||||
"codelyzer": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.2.tgz",
|
||||
"integrity": "sha512-v3+E0Ucu2xWJMOJ2fA/q9pDT/hlxHftHGPUay1/1cTgyPV5JTHFdO9hqo837Sx2s9vKBMTt5gO+lhF95PO6J+g==",
|
||||
"requires": {
|
||||
"@angular/compiler": "9.0.0",
|
||||
"@angular/core": "9.0.0",
|
||||
"app-root-path": "^3.0.0",
|
||||
"aria-query": "^3.0.0",
|
||||
"axobject-query": "2.0.2",
|
||||
"css-selector-tokenizer": "^0.7.1",
|
||||
"cssauron": "^1.4.0",
|
||||
"damerau-levenshtein": "^1.0.4",
|
||||
"rxjs": "^6.5.3",
|
||||
"semver-dsl": "^1.0.1",
|
||||
"source-map": "^0.5.7",
|
||||
"sprintf-js": "^1.1.2",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.10.3"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"zone.js": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz",
|
||||
"integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ansi-colors": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||
|
@ -17428,7 +17574,6 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
|
||||
"integrity": "sha512-majUxHgLehQTeSA+hClx+DY09OVUqG3GtezWkF1krgLGNdlDu9l9V8DaqNMWbq4Eddc8wsyDA0hpDUtnYxQEXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ast-types-flow": "0.0.7",
|
||||
"commander": "^2.11.0"
|
||||
|
@ -17485,8 +17630,7 @@
|
|||
"ast-types-flow": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
|
||||
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
|
||||
"dev": true
|
||||
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
|
@ -17536,7 +17680,6 @@
|
|||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
|
||||
"integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ast-types-flow": "0.0.7"
|
||||
}
|
||||
|
@ -18156,8 +18299,7 @@
|
|||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"commondir": {
|
||||
"version": "1.0.1",
|
||||
|
@ -18610,7 +18752,6 @@
|
|||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
|
||||
"integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cssesc": "^3.0.0",
|
||||
"fastparse": "^1.1.2"
|
||||
|
@ -18626,7 +18767,6 @@
|
|||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz",
|
||||
"integrity": "sha512-Ht70DcFBh+/ekjVrYS2PlDMdSQEl3OFNmjK6lcn49HptBgilXf/Zwg4uFh9Xn0pX3Q8YOkSjIFOfK2osvdqpBw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"through": "X.X.X"
|
||||
}
|
||||
|
@ -18640,8 +18780,7 @@
|
|||
"cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
|
||||
},
|
||||
"custom-event": {
|
||||
"version": "1.0.1",
|
||||
|
@ -18652,8 +18791,7 @@
|
|||
"damerau-levenshtein": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
||||
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
|
@ -19601,8 +19739,7 @@
|
|||
"fastparse": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
||||
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ=="
|
||||
},
|
||||
"fastq": {
|
||||
"version": "1.13.0",
|
||||
|
@ -20186,6 +20323,11 @@
|
|||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
||||
"dev": true
|
||||
},
|
||||
"humanize-duration": {
|
||||
"version": "3.27.3",
|
||||
"resolved": "https://registry.npmjs.org/humanize-duration/-/humanize-duration-3.27.3.tgz",
|
||||
"integrity": "sha512-iimHkHPfIAQ8zCDQLgn08pRqSVioyWvnGfaQ8gond2wf7Jq2jJ+24ykmnRyiz3fIldcn4oUuQXpjqKLhSVR7lw=="
|
||||
},
|
||||
"humanize-ms": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
|
||||
|
@ -21684,6 +21826,14 @@
|
|||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"ngx-dropzone": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ngx-dropzone/-/ngx-dropzone-3.1.0.tgz",
|
||||
"integrity": "sha512-5RBaEl07QUcY6sv/BBPyIxN6nbWY/KqTGheEKgbuGS0N1QPFY7NJUo8+X3fYUwQgLS+wjJeqPiR37dd0YNDtWA==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"nice-napi": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",
|
||||
|
@ -23569,7 +23719,6 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz",
|
||||
"integrity": "sha512-e8BOaTo007E3dMuQQTnPdalbKTABKNS7UxoBIDnwOqRa+QwMrCPjynB8zAlPF6xlqUfdLPPLIJ13hJNmhtq8Ng==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"semver": "^5.3.0"
|
||||
},
|
||||
|
@ -23577,8 +23726,7 @@
|
|||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -24005,8 +24153,7 @@
|
|||
"sprintf-js": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
|
||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
|
||||
"dev": true
|
||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.17.0",
|
||||
|
@ -24232,8 +24379,7 @@
|
|||
"through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
|
||||
},
|
||||
"thunky": {
|
||||
"version": "1.1.0",
|
||||
|
|
|
@ -28,10 +28,14 @@
|
|||
"@fortawesome/free-solid-svg-icons": "^6.2.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "10.0.0",
|
||||
"@panva/oauth4webapi": "^1.1.3",
|
||||
"angular-datatables": "^14.0.0",
|
||||
"bootstrap": "^4.4.1",
|
||||
"chart.js": "2.9.4",
|
||||
"fhirclient": "^2.5.1",
|
||||
"humanize-duration": "^3.27.3",
|
||||
"moment": "^2.29.4",
|
||||
"ng2-charts": "^2.3.0",
|
||||
"ngx-dropzone": "^3.1.0",
|
||||
"rxjs": "~6.5.4",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.11.8"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
@ -18,7 +17,7 @@ import { ResourceDetailComponent } from './pages/resource-detail/resource-detail
|
|||
import { AuthSignupComponent } from './pages/auth-signup/auth-signup.component';
|
||||
import { AuthSigninComponent } from './pages/auth-signin/auth-signin.component';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { NgxDropzoneModule } from 'ngx-dropzone';
|
||||
import { AuthInterceptorService } from './services/auth-interceptor.service';
|
||||
import { CanActivateAuthGuard } from './services/can-activate.auth-guard';
|
||||
import {FastenApiService} from './services/fasten-api.service';
|
||||
|
@ -44,7 +43,8 @@ import { SourceDetailComponent } from './pages/source-detail/source-detail.compo
|
|||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
NgbModule,
|
||||
ChartsModule
|
||||
ChartsModule,
|
||||
NgxDropzoneModule
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
|
|
@ -1 +1,31 @@
|
|||
<p>list-care-plan works!</p>
|
||||
<p class="mg-b-20">A clinical condition, problem, diagnosis, or other event, situation, issue, or clinical concept that has risen to a level of concern.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered mg-b-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Category</th>
|
||||
<th>Reason</th>
|
||||
<th>Period</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let careplan of careplanList">
|
||||
<td>
|
||||
<b>{{careplan.category}}</b>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<div *ngFor="let reason of careplan.reason">
|
||||
{{reason[0]}}
|
||||
<span> - </span>
|
||||
<span className="text-muted">
|
||||
{{reason[1] || "no data"}}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle">{{careplan.period?.start}} - {{careplan.period?.end}}</td>
|
||||
<td class="align-middle">{{careplan.status}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {CarePlan} from '../../models/display/care-plan';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-care-plan',
|
||||
|
@ -7,9 +9,17 @@ import { Component, OnInit } from '@angular/core';
|
|||
})
|
||||
export class ListCarePlanComponent implements OnInit {
|
||||
|
||||
@Input() resourceList: ResourceFhir[] = []
|
||||
careplanList: CarePlan[] = []
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
let _careplanList = this.careplanList
|
||||
this.resourceList.forEach((resource) => {
|
||||
let careplan = new CarePlan(resource.payload)
|
||||
_careplanList.push(careplan)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
<tr *ngFor="let condition of conditionList">
|
||||
<td>
|
||||
{{condition.name}}
|
||||
<small class="text-muted pull-right">
|
||||
<small class="text-muted float-right">
|
||||
{{ condition.nameCode}} {{ condition.nameCodeSystem }}
|
||||
</small>
|
||||
</td>
|
||||
<td>{{condition.clinicalStatus}}</td>
|
||||
<td>{{condition.verificationStatus}}</td>
|
||||
<td>{{condition.onset}}</td>
|
||||
<td class="align-middle">{{condition.clinicalStatus}}</td>
|
||||
<td class="align-middle">{{condition.verificationStatus}}</td>
|
||||
<td class="align-middle">{{condition.onset | date:'M/d/yy'}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,51 +1,6 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {getCodeOrConcept} from '../../fhir/utils';
|
||||
import {CODE_SYSTEMS} from '../../fhir/constants';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
|
||||
|
||||
export class Condition {
|
||||
name: string
|
||||
nameCode: string
|
||||
nameCodeSystem: string
|
||||
clinicalStatus: string
|
||||
verificationStatus: string
|
||||
onset: string
|
||||
|
||||
constructor(resourcePayload: any) {
|
||||
this.populateConditionName(resourcePayload)
|
||||
this.clinicalStatus = getCodeOrConcept(resourcePayload.clinicalStatus)
|
||||
this.verificationStatus = getCodeOrConcept(resourcePayload.verificationStatus)
|
||||
|
||||
}
|
||||
|
||||
populateConditionName(resourePayload: any){
|
||||
if (resourePayload.code) {
|
||||
if (resourePayload.code.text) {
|
||||
this.name = resourePayload.code.text;
|
||||
}
|
||||
if (Array.isArray(resourePayload.code.coding) && resourePayload.code.coding.length) {
|
||||
let c = resourePayload.code.coding[0]
|
||||
|
||||
this.nameCodeSystem = c.system
|
||||
for (let key in CODE_SYSTEMS) {
|
||||
if (CODE_SYSTEMS[key].url === c.system) {
|
||||
this.nameCodeSystem = `(${key})`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c.display) {
|
||||
this.name = c.display
|
||||
}
|
||||
if (c.code) {
|
||||
this.nameCode = c.code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
import {Condition} from '../../models/display/condition';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-condition',
|
||||
|
@ -55,21 +10,16 @@ export class Condition {
|
|||
export class ListConditionComponent implements OnInit {
|
||||
|
||||
@Input() resourceList: ResourceFhir[] = []
|
||||
|
||||
conditionList: Condition[] = []
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
console.log("INSIDE LIST CONDIDITION", this.resourceList)
|
||||
let _conditions = this.conditionList
|
||||
this.resourceList.forEach((resource) => {
|
||||
let cond = new Condition(resource.payload)
|
||||
_conditions.push(cond)
|
||||
console.log("PARSED CONDITITION", cond)
|
||||
})
|
||||
|
||||
console.log("COMPLETED", this.conditionList)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +1,27 @@
|
|||
<p>list-encounter works!</p>
|
||||
<p class="mg-b-20">A clinical condition, problem, diagnosis, or other event, situation, issue, or clinical concept that has risen to a level of concern.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered mg-b-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Reason</th>
|
||||
<th>Class</th>
|
||||
<th>Status</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let encounter of encounterList">
|
||||
<td>
|
||||
<b *ngIf="encounter.encounterType">{{encounter.encounterType}}</b>
|
||||
<span *ngIf="!encounter.encounterType && encounter.encounterClass" class="text-muted">{{encounter.encounterClass}} encounter</span>
|
||||
<small *ngIf="!encounter.encounterType && !encounter.encounterClass" class="text-muted">N/A</small>
|
||||
</td>
|
||||
<td class="align-middle">{{encounter.reason ? encounter.reason : 'N/A' }}</td>
|
||||
<td class="align-middle">{{encounter.encounterClass ? encounter.encounterClass : 'N/A' }}</td>
|
||||
<td class="align-middle">{{encounter.status ? encounter.status : 'N/A'}}</td>
|
||||
<td class="align-middle">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {Condition} from '../../models/display/condition';
|
||||
import {Encounter} from '../../models/display/encounter';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-encounter',
|
||||
|
@ -7,9 +10,16 @@ import { Component, OnInit } from '@angular/core';
|
|||
})
|
||||
export class ListEncounterComponent implements OnInit {
|
||||
|
||||
@Input() resourceList: ResourceFhir[] = []
|
||||
encounterList: Encounter[] = []
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
let _encounterList = this.encounterList
|
||||
this.resourceList.forEach((resource) => {
|
||||
let encounter = new Encounter(resource.payload)
|
||||
_encounterList.push(encounter)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +1,19 @@
|
|||
<p>list-immunization works!</p>
|
||||
<p class="mg-b-20">A clinical condition, problem, diagnosis, or other event, situation, issue, or clinical concept that has risen to a level of concern.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered mg-b-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let immunization of immunizationList">
|
||||
<td>{{immunization.immunizationType}}</td>
|
||||
<td class="align-middle">{{immunization.status || '-'}}</td>
|
||||
<td class="align-middle">{{immunization.date }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {Encounter} from '../../models/display/encounter';
|
||||
import {Immunization} from '../../models/display/immunization';
|
||||
|
||||
@Component({
|
||||
selector: 'app-list-immunization',
|
||||
|
@ -7,9 +10,17 @@ import { Component, OnInit } from '@angular/core';
|
|||
})
|
||||
export class ListImmunizationComponent implements OnInit {
|
||||
|
||||
@Input() resourceList: ResourceFhir[] = []
|
||||
immunizationList: Immunization[] = []
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
let _immunizationList = this.immunizationList
|
||||
this.resourceList.forEach((resource) => {
|
||||
let immunization = new Immunization(resource.payload)
|
||||
_immunizationList.push(immunization)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { ListEncounterComponent } from './list-encounter/list-encounter.componen
|
|||
import { ListConditionComponent } from './list-condition/list-condition.component';
|
||||
import { ListCarePlanComponent } from './list-care-plan/list-care-plan.component';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import { ListGenericResourceComponent } from './list-generic-resource/list-generic-resource.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -25,12 +26,19 @@ import {BrowserModule} from '@angular/platform-browser';
|
|||
ListImmunizationComponent,
|
||||
ListEncounterComponent,
|
||||
ListConditionComponent,
|
||||
ListCarePlanComponent
|
||||
ListCarePlanComponent,
|
||||
ListGenericResourceComponent
|
||||
],
|
||||
exports: [
|
||||
ComponentsSidebarComponent,
|
||||
UtilitiesSidebarComponent,
|
||||
ListConditionComponent
|
||||
ComponentsSidebarComponent,
|
||||
UtilitiesSidebarComponent,
|
||||
ListPatientComponent,
|
||||
ListObservationComponent,
|
||||
ListExplanationOfBenefitComponent,
|
||||
ListImmunizationComponent,
|
||||
ListEncounterComponent,
|
||||
ListConditionComponent,
|
||||
ListCarePlanComponent
|
||||
]
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import {getCodeOrConcept} from '../../fhir/utils';
|
||||
import {CODE_SYSTEMS} from '../../fhir/constants';
|
||||
|
||||
export class Condition {
|
||||
name: string
|
||||
nameCode: string
|
||||
nameCodeSystem: string
|
||||
clinicalStatus: string
|
||||
verificationStatus: string
|
||||
onset: string
|
||||
|
||||
constructor(resourcePayload: any) {
|
||||
this.populateConditionName(resourcePayload)
|
||||
this.clinicalStatus = getCodeOrConcept(resourcePayload.clinicalStatus)
|
||||
this.verificationStatus = getCodeOrConcept(resourcePayload.verificationStatus)
|
||||
this.onset = resourcePayload.onsetDateTime
|
||||
}
|
||||
|
||||
populateConditionName(resourePayload: any){
|
||||
if (resourePayload.code) {
|
||||
if (resourePayload.code.text) {
|
||||
this.name = resourePayload.code.text;
|
||||
}
|
||||
if (Array.isArray(resourePayload.code.coding) && resourePayload.code.coding.length) {
|
||||
let c = resourePayload.code.coding[0]
|
||||
|
||||
this.nameCodeSystem = c.system
|
||||
for (let key in CODE_SYSTEMS) {
|
||||
if (CODE_SYSTEMS[key].url === c.system) {
|
||||
this.nameCodeSystem = `(${key})`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c.display) {
|
||||
this.name = c.display
|
||||
}
|
||||
if (c.code) {
|
||||
this.nameCode = c.code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import {getPath} from '../../fhir/utils';
|
||||
import * as moment from 'moment'
|
||||
|
||||
export class Immunization {
|
||||
immunizationType: string
|
||||
status: string
|
||||
date: moment.Moment
|
||||
|
||||
constructor(resourcePayload: any) {
|
||||
this.immunizationType = getPath(resourcePayload, "vaccineCode.coding.0.display")
|
||||
this.status = resourcePayload.status || "-"
|
||||
this.date = moment(resourcePayload.date || resourcePayload.occurrenceDateTime || resourcePayload.occurrenceString)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import * as moment from 'moment';
|
||||
import {getPath} from '../../fhir/utils';
|
||||
|
||||
export class Observation {
|
||||
code: string
|
||||
status: string
|
||||
date: moment.Moment
|
||||
|
||||
// { title: 'Observation', versions: '*', format: 'code', getter: o => o.code.coding[0] },
|
||||
// { title: 'Value', versions: '*', getter: o => obsValue(o) },
|
||||
// { title: 'Effective', 'versions': '*', getter: o => attributeXTime(o,'effective') },
|
||||
// { title: 'Issued Date', 'versions': '*', format: 'date', getter: o => o.issued },
|
||||
// { title: 'ID', versions: '*', getter: o => o.id }
|
||||
|
||||
constructor(resourcePayload: any) {
|
||||
this.code = getPath(resourcePayload, "code.coding[0].display")
|
||||
this.status = resourcePayload.status || "-"
|
||||
this.date = moment(resourcePayload.date || resourcePayload.occurrenceDateTime || resourcePayload.occurrenceString)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -20,19 +20,18 @@
|
|||
|
||||
<h2 class="az-content-title mg-t-40">Medical Sources</h2>
|
||||
|
||||
<div class="az-content-label mg-b-5">Hospitals & Medical Clinics</div>
|
||||
<div class="az-content-label mg-b-5">Manual Upload</div>
|
||||
<p class="mg-b-20">The following medical insurance companies have API's which Fasten can use to retrieve your medical history.
|
||||
Please click the logos below to initiate the connection.</p>
|
||||
|
||||
<div class="row row-sm">
|
||||
<div class="col-lg">
|
||||
<input class="form-control" placeholder="Input box" type="text">
|
||||
</div><!-- col -->
|
||||
<div class="col-lg mg-t-10 mg-lg-t-0">
|
||||
<input class="form-control" placeholder="Input box (readonly)" readonly="" type="text">
|
||||
</div><!-- col -->
|
||||
<div class="col-lg mg-t-10 mg-lg-t-0">
|
||||
<input class="form-control" placeholder="Input box (disabled)" disabled="" type="text">
|
||||
<ngx-dropzone [multiple]="false" (change)="uploadSourceBundle($event)">
|
||||
<ngx-dropzone-label>Drop it, baby!</ngx-dropzone-label>
|
||||
<ngx-dropzone-preview *ngFor="let f of uploadedFile" [removable]="false">
|
||||
<ngx-dropzone-label>{{ f.name }} ({{ f.type }})</ngx-dropzone-label>
|
||||
</ngx-dropzone-preview>
|
||||
</ngx-dropzone>
|
||||
</div><!-- col -->
|
||||
</div><!-- row -->
|
||||
|
||||
|
|
|
@ -39,8 +39,7 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
connectedSourceList = []
|
||||
availableSourceList = []
|
||||
|
||||
|
||||
|
||||
uploadedFile: File[] = []
|
||||
|
||||
ngOnInit(): void {
|
||||
this.fastenApi.getSources()
|
||||
|
@ -193,6 +192,20 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
uploadSourceBundle(event) {
|
||||
this.uploadedFile = [event.addedFiles[0]]
|
||||
this.fastenApi.createManualSource(event.addedFiles[0]).subscribe(
|
||||
(respData) => {
|
||||
console.log("source manual source create response:", respData)
|
||||
},
|
||||
(err) => {console.log(err)},
|
||||
() => {
|
||||
this.uploadedFile = []
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
uuidV4(){
|
||||
// @ts-ignore
|
||||
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
|
||||
|
|
|
@ -17,27 +17,10 @@
|
|||
<h2 class="az-content-title mg-t-40">{{selectedResourceType}}</h2>
|
||||
|
||||
<app-list-condition *ngIf="selectedResourceType == 'Condition'" [resourceList]="selectedResources"></app-list-condition>
|
||||
<app-list-care-plan *ngIf="selectedResourceType == 'CarePlan'" [resourceList]="selectedResources"></app-list-care-plan>
|
||||
<app-list-encounter *ngIf="selectedResourceType == 'Encounter'" [resourceList]="selectedResources"></app-list-encounter>
|
||||
<app-list-immunization *ngIf="selectedResourceType == 'Immunization'" [resourceList]="selectedResources"></app-list-immunization>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered mg-b-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Position</th>
|
||||
<th>Salary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let resource of selectedResources">
|
||||
<th scope="row">1</th>
|
||||
<td>Tiger Nixon</td>
|
||||
<td>System Architect</td>
|
||||
<td>$320,800</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div><!-- az-content-body -->
|
||||
</div><!-- container -->
|
||||
|
|
|
@ -76,6 +76,20 @@ export class FastenApiService {
|
|||
);
|
||||
}
|
||||
|
||||
createManualSource(file: File): Observable<Source> {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
return this._httpClient.post<any>(`${this.getBasePath()}/api/secure/source/manual`, formData)
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
console.log("MANUAL SOURCE RESPONSE", response)
|
||||
return response.data as Source
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getSources(): Observable<Source[]> {
|
||||
return this._httpClient.get<any>(`${this.getBasePath()}/api/secure/source`)
|
||||
.pipe(
|
||||
|
|
Loading…
Reference in New Issue