From d23af018e7ada6d1c1f9e839788ed5e69d7f765c Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Sun, 3 Dec 2023 18:40:16 -0800 Subject: [PATCH] Timeline based Manual Entry form (#330) --- backend/pkg/database/gorm_common.go | 150 +++- .../database/gorm_repository_migrations.go | 30 +- backend/pkg/database/gorm_repository_test.go | 2 +- backend/pkg/database/interface.go | 13 +- backend/pkg/models/source_credential.go | 3 + backend/pkg/web/handler/background_jobs.go | 72 +- backend/pkg/web/handler/resource_fhir.go | 1 + backend/pkg/web/handler/resource_related.go | 95 +++ backend/pkg/web/handler/source.go | 87 ++- backend/pkg/web/server.go | 2 + frontend/package.json | 1 + frontend/src/app/app.component.ts | 15 +- frontend/src/app/app.module.ts | 4 + .../common/badge/badge.component.html | 0 .../common/badge/badge.component.scss | 0 .../common/badge/badge.component.spec.ts | 0 .../common/badge/badge.component.ts | 0 .../common/badge/badge.stories.ts | 2 +- .../common/table/table-row-item.spec.ts | 0 .../common/table/table-row-item.ts | 0 .../common/table/table.component.html | 0 .../common/table/table.component.scss | 0 .../common/table/table.component.spec.ts | 0 .../common/table/table.component.ts | 0 .../common/table/table.stories.ts | 2 +- .../binary-text/binary-text.component.html | 0 .../binary-text/binary-text.component.scss | 0 .../binary-text/binary-text.component.spec.ts | 0 .../binary-text/binary-text.component.ts | 0 .../binary-text/binary-text.stories.ts | 2 +- .../codable-concept.component.html | 0 .../codable-concept.component.scss | 0 .../codable-concept.component.spec.ts | 0 .../codable-concept.component.ts | 0 .../datatypes/coding/coding.component.html | 0 .../datatypes/coding/coding.component.scss | 0 .../datatypes/coding/coding.component.spec.ts | 0 .../datatypes/coding/coding.component.ts | 0 .../datatypes/dicom/dicom.component.html | 0 .../datatypes/dicom/dicom.component.scss | 0 .../datatypes/dicom/dicom.component.spec.ts | 0 .../datatypes/dicom/dicom.component.ts | 0 .../datatypes/dicom/dicom.stories.ts | 2 +- .../datatypes/html/html.component.html | 0 .../datatypes/html/html.component.scss | 0 .../datatypes/html/html.component.spec.ts | 0 .../datatypes/html/html.component.ts | 0 .../datatypes/html/html.stories.ts | 2 +- .../datatypes/img/img.component.html | 0 .../datatypes/img/img.component.scss | 0 .../datatypes/img/img.component.spec.ts | 0 .../datatypes/img/img.component.ts | 0 .../datatypes/img/img.stories.ts | 2 +- .../markdown/markdown.component.html | 0 .../markdown/markdown.component.scss | 0 .../markdown/markdown.component.spec.ts | 0 .../datatypes/markdown/markdown.component.ts | 0 .../datatypes/markdown/markdown.stories.ts | 45 ++ .../datatypes/pdf/pdf.component.html | 0 .../datatypes/pdf/pdf.component.scss | 0 .../datatypes/pdf/pdf.component.spec.ts | 0 .../datatypes/pdf/pdf.component.ts | 0 .../datatypes/pdf/pdf.stories.ts | 2 +- .../components/fhir-card/fhir-card.module.ts | 106 +++ .../fhir-card-component-interface.ts} | 3 +- .../fhir-card-outlet.directive.spec.ts} | 6 +- .../fhir-card/fhir-card-outlet.directive.ts} | 4 +- .../fhir-card/fhir-card.component.html | 1 + .../fhir-card/fhir-card.component.scss} | 0 .../fhir-card/fhir-card.component.spec.ts | 24 + .../fhir-card/fhir-card.component.ts} | 29 +- .../allergy-intolerance.component.html | 0 .../allergy-intolerance.component.scss | 0 .../allergy-intolerance.component.spec.ts | 0 .../allergy-intolerance.component.ts | 7 +- .../allergy-intolerance.stories.ts | 10 +- .../resources/binary/binary.component.html | 0 .../resources/binary/binary.component.scss | 0 .../resources/binary/binary.component.spec.ts | 0 .../resources/binary/binary.component.ts | 5 +- .../resources/binary/binary.stories.ts | 2 +- .../diagnostic-report.component.html | 0 .../diagnostic-report.component.scss | 0 .../diagnostic-report.component.spec.ts | 4 +- .../diagnostic-report.component.ts | 19 +- .../document-reference.component.html | 0 .../document-reference.component.scss | 0 .../document-reference.component.spec.ts | 5 +- .../document-reference.component.ts | 18 +- .../encounter/encounter.component.html | 19 + .../encounter/encounter.component.scss} | 0 .../encounter/encounter.component.spec.ts | 24 + .../encounter/encounter.component.ts | 50 ++ .../resources/encounter/encounter.stories.ts | 70 ++ .../fallback/fallback.component.html | 0 .../fallback/fallback.component.scss} | 0 .../fallback/fallback.component.spec.ts | 2 +- .../resources/fallback/fallback.component.ts | 10 +- .../immunization/immunization.component.html | 0 .../immunization/immunization.component.scss} | 0 .../immunization.component.spec.ts | 0 .../immunization/immunization.component.ts | 7 +- .../immunization/immunization.stories.ts | 2 +- .../location/location.component.html | 0 .../location/location.component.scss} | 0 .../location/location.component.spec.ts | 3 +- .../resources/location/location.component.ts | 6 +- .../resources/location/location.stories.ts | 59 ++ .../resources/media/media.component.html | 0 .../resources/media/media.component.scss} | 0 .../resources/media/media.component.spec.ts | 4 +- .../resources/media/media.component.ts | 17 +- .../medication-request.component.html | 0 .../medication-request.component.scss} | 0 .../medication-request.component.spec.ts | 0 .../medication-request.component.ts | 8 +- .../medication-request.stories.ts | 2 +- .../medication/medication.component.html | 0 .../medication/medication.component.scss} | 0 .../medication/medication.component.spec.ts | 0 .../medication/medication.component.ts | 7 +- .../medication/medication.stories.ts | 2 +- .../observation/observation.component.html | 0 .../observation/observation.component.scss} | 0 .../observation/observation.component.spec.ts | 3 +- .../observation/observation.component.ts | 13 +- .../observation/observation.stories.ts | 69 ++ .../organization/organization.component.html | 6 +- .../organization/organization.component.scss} | 0 .../organization.component.spec.ts | 3 +- .../organization/organization.component.ts | 2 +- .../organization/organization.stories.ts | 69 ++ .../practitioner/practitioner.component.html | 10 +- .../practitioner/practitioner.component.scss} | 0 .../practitioner.component.spec.ts | 0 .../practitioner/practitioner.component.ts | 13 +- .../practitioner/practitioner.stories.ts | 2 +- .../procedure/procedure.component.html | 0 .../procedure/procedure.component.scss} | 0 .../procedure/procedure.component.spec.ts | 0 .../procedure/procedure.component.ts | 7 +- .../resources/procedure/procedure.stories.ts | 2 +- .../datatable-adverse-event.component.ts | 15 + ...atatable-allergy-intolerance.component.ts} | 10 +- .../datatable-appointment.component.ts} | 10 +- .../datatable-binary.component.ts} | 10 +- .../datatable-care-plan.component.ts} | 10 +- .../datatable-care-team.component.ts} | 10 +- .../datatable-communication.component.ts} | 10 +- .../datatable-condition.component.ts} | 10 +- .../datatable-coverage.component.ts | 15 + .../datatable-device-request.component.ts} | 10 +- .../datatable-device.component.ts} | 10 +- .../datatable-diagnostic-report.component.ts} | 10 +- ...datatable-document-reference.component.ts} | 10 +- .../datatable-encounter.component.ts} | 10 +- ...atable-explanation-of-benefit.component.ts | 11 + .../datatable-fallback.component.html} | 8 - .../datatable-fallback.component.ts | 14 + .../datatable-generic-resource.component.html | 21 + ...datatable-generic-resource.component.scss} | 0 ...atable-generic-resource.component.spec.ts} | 12 +- .../datatable-generic-resource.component.ts} | 45 +- .../datatable-goal.component.ts | 15 + .../datatable-immunization.component.ts} | 10 +- .../datatable-location.component.ts} | 10 +- ...le-medication-administration.component.ts} | 10 +- ...datatable-medication-dispense.component.ts | 15 + ...datatable-medication-request.component.ts} | 10 +- .../datatable-medication.component.ts | 15 + .../datatable-nutrition-order.component.ts} | 10 +- .../datatable-observation.component.ts} | 10 +- .../datatable-organization.component.ts} | 10 +- .../datatable-practitioner.component.ts} | 10 +- .../datatable-procedure.component.ts} | 10 +- .../datatable-service-request.component.ts} | 10 +- .../datatable-generic-resource}/utils.ts | 3 + .../fhir-datatable/fhir-datatable.module.ts | 111 +++ .../fhir-datatable-outlet.directive.spec.ts} | 2 +- .../fhir-datatable-outlet.directive.ts} | 4 +- .../fhir-datatable.component.html | 25 + .../fhir-datatable.component.scss} | 0 .../fhir-datatable.component.spec.ts} | 16 +- .../fhir-datatable.component.ts | 182 +++++ .../list-patient/list-patient.component.html | 0 .../list-patient/list-patient.component.scss | 0 .../list-patient.component.spec.ts | 0 .../list-patient/list-patient.component.ts | 0 .../fhir-resource.component.html | 1 - .../fhir-resource.component.spec.ts | 24 - .../list-adverse-event.component.ts | 15 - .../list-coverage.component.ts | 15 - .../list-explanation-of-benefit.component.ts | 11 - .../list-fallback.component.ts | 14 - .../list-generic-resource.component.html | 32 - .../list-goal.component.ts | 15 - .../list-medication-dispense.component.ts | 15 - .../list-medication.component.ts | 15 - ...ecord-wizard-add-attachment.component.html | 40 ++ ...ecord-wizard-add-attachment.component.scss | 0 ...rd-wizard-add-attachment.component.spec.ts | 32 + ...-record-wizard-add-attachment.component.ts | 67 ++ ...al-record-wizard-add-attachment.stories.ts | 46 ++ ...record-wizard-add-encounter.component.html | 76 +++ ...record-wizard-add-encounter.component.scss | 0 ...ord-wizard-add-encounter.component.spec.ts | 31 + ...l-record-wizard-add-encounter.component.ts | 158 +++++ ...cal-record-wizard-add-encounter.stories.ts | 46 ++ ...ord-wizard-add-organization.component.html | 108 +++ ...ord-wizard-add-organization.component.scss | 0 ...-wizard-add-organization.component.spec.ts | 31 + ...ecord-wizard-add-organization.component.ts | 232 +++++++ ...-record-wizard-add-organization.stories.ts | 46 ++ ...ord-wizard-add-practitioner.component.html | 110 +++ ...ord-wizard-add-practitioner.component.scss | 0 ...-wizard-add-practitioner.component.spec.ts | 31 + ...ecord-wizard-add-practitioner.component.ts | 241 +++++++ ...-record-wizard-add-practitioner.stories.ts | 46 ++ .../medical-record-wizard.component.html | 428 ++++++++++++ .../medical-record-wizard.component.scss | 0 .../medical-record-wizard.component.spec.ts | 31 + .../medical-record-wizard.component.ts | 398 +++++++++++ .../medical-record-wizard.stories.ts | 46 ++ .../medical-record-wizard.utilities.ts | 443 ++++++++++++ .../medical-sources-card.component.ts | 2 + .../medical-sources-connected.component.html | 16 +- .../nlm-typeahead.component.spec.ts | 3 +- .../nlm-typeahead/nlm-typeahead.component.ts | 28 +- .../nlm-typeahead/nlm-typeahead.stories.ts | 169 +++++ .../report-header.component.html | 2 +- ...t-medical-history-condition.component.html | 12 +- ...ort-medical-history-condition.component.ts | 4 +- ...report-medical-history-editor.component.ts | 3 + ...tory-explanation-of-benefit.component.html | 12 +- ...istory-explanation-of-benefit.component.ts | 4 + ...ical-history-timeline-panel.component.html | 32 +- ...l-history-timeline-panel.component.spec.ts | 2 + ...edical-history-timeline-panel.component.ts | 22 +- .../resource-list.component.html | 3 - .../resource-list/resource-list.component.ts | 174 ----- frontend/src/app/components/shared.module.ts | 209 +----- .../src/app/models/fasten/resource_create.ts | 14 +- .../pages/dashboard/dashboard.component.ts | 2 +- .../medical-history.component.spec.ts | 3 +- .../medical-history.component.ts | 2 +- .../medical-sources.component.html | 2 +- .../patient-profile.component.html | 4 +- .../report-labs/report-labs.component.html | 2 +- .../resource-creator.component.html | 642 ------------------ .../resource-creator.component.ts | 460 +------------ .../resource-creator.utilities.ts | 534 --------------- .../resource-detail.component.html | 2 +- .../source-detail.component.html | 10 +- .../source-detail/source-detail.component.ts | 2 +- .../src/app/pipes/human-name.pipe.spec.ts | 8 + frontend/src/app/pipes/human-name.pipe.ts | 36 + frontend/src/app/pipes/pipes.module.ts | 8 +- .../src/app/pipes/reference-uri.pipe.spec.ts | 8 + frontend/src/app/pipes/reference-uri.pipe.ts | 18 + .../src/app/services/fasten-api.service.ts | 28 +- .../nlm-clinical-table-search.service.ts | 93 +++ frontend/src/assets/scss/custom/_modal.scss | 2 +- frontend/src/assets/scss/custom/_nav.scss | 488 ++++++------- frontend/src/assets/sources/fasten.png | Bin 0 -> 7083 bytes frontend/src/custom.scss | 55 ++ .../r4/resources/binary/exampleMarkdown.json | 9 + .../r4/resources/binary/exampleRtf.json | 9 + .../resources/diagnosticReport/example1.json | 30 +- .../models/datatypes/codable-concept-model.ts | 2 +- .../lib/models/datatypes/human-name-model.ts | 2 +- frontend/src/lib/models/factory.ts | 7 +- .../resources/diagnostic-report-model.spec.ts | 15 +- .../resources/diagnostic-report-model.ts | 2 +- .../models/resources/encounter-model.spec.ts | 9 +- .../models/resources/organization-model.ts | 5 +- .../models/resources/practitioner-model.ts | 6 +- frontend/src/lib/utils/bundle_references.ts | 43 ++ frontend/yarn.lock | 5 + go.mod | 13 +- go.sum | 26 +- 280 files changed, 5336 insertions(+), 2828 deletions(-) create mode 100644 backend/pkg/web/handler/resource_related.go rename frontend/src/app/components/{fhir => fhir-card}/common/badge/badge.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/badge/badge.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/badge/badge.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/badge/badge.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/badge/badge.stories.ts (96%) rename frontend/src/app/components/{fhir => fhir-card}/common/table/table-row-item.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/table/table-row-item.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/table/table.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/table/table.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/table/table.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/table/table.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/common/table/table.stories.ts (98%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/binary-text/binary-text.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/binary-text/binary-text.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/binary-text/binary-text.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/binary-text/binary-text.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/binary-text/binary-text.stories.ts (96%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/codable-concept/codable-concept.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/codable-concept/codable-concept.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/codable-concept/codable-concept.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/codable-concept/codable-concept.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/coding/coding.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/coding/coding.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/coding/coding.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/coding/coding.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/dicom/dicom.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/dicom/dicom.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/dicom/dicom.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/dicom/dicom.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/dicom/dicom.stories.ts (97%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/html/html.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/html/html.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/html/html.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/html/html.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/html/html.stories.ts (97%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/img/img.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/img/img.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/img/img.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/img/img.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/img/img.stories.ts (97%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/markdown/markdown.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/markdown/markdown.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/markdown/markdown.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/markdown/markdown.component.ts (100%) create mode 100644 frontend/src/app/components/fhir-card/datatypes/markdown/markdown.stories.ts rename frontend/src/app/components/{fhir => fhir-card}/datatypes/pdf/pdf.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/pdf/pdf.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/pdf/pdf.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/pdf/pdf.component.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/datatypes/pdf/pdf.stories.ts (97%) create mode 100644 frontend/src/app/components/fhir-card/fhir-card.module.ts rename frontend/src/app/components/{fhir/fhir-resource/fhir-resource-component-interface.ts => fhir-card/fhir-card/fhir-card-component-interface.ts} (84%) rename frontend/src/app/components/{fhir/fhir-resource/fhir-resource-outlet.directive.spec.ts => fhir-card/fhir-card/fhir-card-outlet.directive.spec.ts} (94%) rename frontend/src/app/components/{resource-list/resource-list-outlet.directive.ts => fhir-card/fhir-card/fhir-card-outlet.directive.ts} (64%) create mode 100644 frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.html rename frontend/src/app/components/{fhir/fhir-resource/fhir-resource.component.scss => fhir-card/fhir-card/fhir-card.component.scss} (100%) create mode 100644 frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.spec.ts rename frontend/src/app/components/{fhir/fhir-resource/fhir-resource.component.ts => fhir-card/fhir-card/fhir-card.component.ts} (85%) rename frontend/src/app/components/{fhir => fhir-card}/resources/allergy-intolerance/allergy-intolerance.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/allergy-intolerance/allergy-intolerance.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/allergy-intolerance/allergy-intolerance.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/allergy-intolerance/allergy-intolerance.component.ts (94%) rename frontend/src/app/components/{fhir => fhir-card}/resources/allergy-intolerance/allergy-intolerance.stories.ts (89%) rename frontend/src/app/components/{fhir => fhir-card}/resources/binary/binary.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/binary/binary.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/binary/binary.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/binary/binary.component.ts (92%) rename frontend/src/app/components/{fhir => fhir-card}/resources/binary/binary.stories.ts (99%) rename frontend/src/app/components/{fhir => fhir-card}/resources/diagnostic-report/diagnostic-report.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/diagnostic-report/diagnostic-report.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/diagnostic-report/diagnostic-report.component.spec.ts (82%) rename frontend/src/app/components/{fhir => fhir-card}/resources/diagnostic-report/diagnostic-report.component.ts (69%) rename frontend/src/app/components/{fhir => fhir-card}/resources/document-reference/document-reference.component.html (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/document-reference/document-reference.component.scss (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/document-reference/document-reference.component.spec.ts (80%) rename frontend/src/app/components/{fhir => fhir-card}/resources/document-reference/document-reference.component.ts (64%) create mode 100644 frontend/src/app/components/fhir-card/resources/encounter/encounter.component.html rename frontend/src/app/components/{fhir/resources/fallback/fallback.component.scss => fhir-card/resources/encounter/encounter.component.scss} (100%) create mode 100644 frontend/src/app/components/fhir-card/resources/encounter/encounter.component.spec.ts create mode 100644 frontend/src/app/components/fhir-card/resources/encounter/encounter.component.ts create mode 100644 frontend/src/app/components/fhir-card/resources/encounter/encounter.stories.ts rename frontend/src/app/components/{fhir => fhir-card}/resources/fallback/fallback.component.html (100%) rename frontend/src/app/components/{fhir/resources/immunization/immunization.component.scss => fhir-card/resources/fallback/fallback.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/fallback/fallback.component.spec.ts (93%) rename frontend/src/app/components/{fhir => fhir-card}/resources/fallback/fallback.component.ts (55%) rename frontend/src/app/components/{fhir => fhir-card}/resources/immunization/immunization.component.html (100%) rename frontend/src/app/components/{fhir/resources/location/location.component.scss => fhir-card/resources/immunization/immunization.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/immunization/immunization.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/immunization/immunization.component.ts (92%) rename frontend/src/app/components/{fhir => fhir-card}/resources/immunization/immunization.stories.ts (98%) rename frontend/src/app/components/{fhir => fhir-card}/resources/location/location.component.html (100%) rename frontend/src/app/components/{fhir/resources/media/media.component.scss => fhir-card/resources/location/location.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/location/location.component.spec.ts (82%) rename frontend/src/app/components/{fhir => fhir-card}/resources/location/location.component.ts (92%) create mode 100644 frontend/src/app/components/fhir-card/resources/location/location.stories.ts rename frontend/src/app/components/{fhir => fhir-card}/resources/media/media.component.html (100%) rename frontend/src/app/components/{fhir/resources/medication-request/medication-request.component.scss => fhir-card/resources/media/media.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/media/media.component.spec.ts (87%) rename frontend/src/app/components/{fhir => fhir-card}/resources/media/media.component.ts (63%) rename frontend/src/app/components/{fhir => fhir-card}/resources/medication-request/medication-request.component.html (100%) rename frontend/src/app/components/{fhir/resources/medication/medication.component.scss => fhir-card/resources/medication-request/medication-request.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/medication-request/medication-request.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/medication-request/medication-request.component.ts (92%) rename frontend/src/app/components/{fhir => fhir-card}/resources/medication-request/medication-request.stories.ts (98%) rename frontend/src/app/components/{fhir => fhir-card}/resources/medication/medication.component.html (100%) rename frontend/src/app/components/{fhir/resources/observation/observation.component.scss => fhir-card/resources/medication/medication.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/medication/medication.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/medication/medication.component.ts (92%) rename frontend/src/app/components/{fhir => fhir-card}/resources/medication/medication.stories.ts (98%) rename frontend/src/app/components/{fhir => fhir-card}/resources/observation/observation.component.html (100%) rename frontend/src/app/components/{fhir/resources/organization/organization.component.scss => fhir-card/resources/observation/observation.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/observation/observation.component.spec.ts (82%) rename frontend/src/app/components/{fhir => fhir-card}/resources/observation/observation.component.ts (88%) create mode 100644 frontend/src/app/components/fhir-card/resources/observation/observation.stories.ts rename frontend/src/app/components/{fhir => fhir-card}/resources/organization/organization.component.html (79%) rename frontend/src/app/components/{fhir/resources/practitioner/practitioner.component.scss => fhir-card/resources/organization/organization.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/organization/organization.component.spec.ts (82%) rename frontend/src/app/components/{fhir => fhir-card}/resources/organization/organization.component.ts (98%) create mode 100644 frontend/src/app/components/fhir-card/resources/organization/organization.stories.ts rename frontend/src/app/components/{fhir => fhir-card}/resources/practitioner/practitioner.component.html (72%) rename frontend/src/app/components/{fhir/resources/procedure/procedure.component.scss => fhir-card/resources/practitioner/practitioner.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/practitioner/practitioner.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/practitioner/practitioner.component.ts (88%) rename frontend/src/app/components/{fhir => fhir-card}/resources/practitioner/practitioner.stories.ts (98%) rename frontend/src/app/components/{fhir => fhir-card}/resources/procedure/procedure.component.html (100%) rename frontend/src/app/components/{list-generic-resource/list-generic-resource.component.scss => fhir-card/resources/procedure/procedure.component.scss} (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/procedure/procedure.component.spec.ts (100%) rename frontend/src/app/components/{fhir => fhir-card}/resources/procedure/procedure.component.ts (93%) rename frontend/src/app/components/{fhir => fhir-card}/resources/procedure/procedure.stories.ts (98%) create mode 100644 frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-adverse-event.component.ts rename frontend/src/app/components/{list-generic-resource/list-allergy-intolerance.component.ts => fhir-datatable/datatable-generic-resource/datatable-allergy-intolerance.component.ts} (66%) rename frontend/src/app/components/{list-generic-resource/list-appointment.component.ts => fhir-datatable/datatable-generic-resource/datatable-appointment.component.ts} (56%) rename frontend/src/app/components/{list-generic-resource/list-binary.component.ts => fhir-datatable/datatable-generic-resource/datatable-binary.component.ts} (55%) rename frontend/src/app/components/{list-generic-resource/list-care-plan.component.ts => fhir-datatable/datatable-generic-resource/datatable-care-plan.component.ts} (64%) rename frontend/src/app/components/{list-generic-resource/list-care-team.component.ts => fhir-datatable/datatable-generic-resource/datatable-care-team.component.ts} (55%) rename frontend/src/app/components/{list-generic-resource/list-communication.component.ts => fhir-datatable/datatable-generic-resource/datatable-communication.component.ts} (56%) rename frontend/src/app/components/{list-generic-resource/list-condition.component.ts => fhir-datatable/datatable-generic-resource/datatable-condition.component.ts} (65%) create mode 100644 frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-coverage.component.ts rename frontend/src/app/components/{list-generic-resource/list-device-request.component.ts => fhir-datatable/datatable-generic-resource/datatable-device-request.component.ts} (60%) rename frontend/src/app/components/{list-generic-resource/list-device.component.ts => fhir-datatable/datatable-generic-resource/datatable-device.component.ts} (63%) rename frontend/src/app/components/{list-generic-resource/list-diagnostic-report.component.ts => fhir-datatable/datatable-generic-resource/datatable-diagnostic-report.component.ts} (57%) rename frontend/src/app/components/{list-generic-resource/list-document-reference.component.ts => fhir-datatable/datatable-generic-resource/datatable-document-reference.component.ts} (61%) rename frontend/src/app/components/{list-generic-resource/list-encounter.component.ts => fhir-datatable/datatable-generic-resource/datatable-encounter.component.ts} (62%) create mode 100644 frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-explanation-of-benefit.component.ts rename frontend/src/app/components/{list-generic-resource/list-fallback.component.html => fhir-datatable/datatable-generic-resource/datatable-fallback.component.html} (56%) create mode 100644 frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-fallback.component.ts create mode 100644 frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.html rename frontend/src/app/components/{list-patient/list-patient.component.scss => fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.scss} (100%) rename frontend/src/app/components/{list-generic-resource/list-generic-resource.component.spec.ts => fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.spec.ts} (61%) rename frontend/src/app/components/{list-generic-resource/list-generic-resource.component.ts => fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.ts} (71%) create mode 100644 frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-goal.component.ts rename frontend/src/app/components/{list-generic-resource/list-immunization.component.ts => fhir-datatable/datatable-generic-resource/datatable-immunization.component.ts} (57%) rename frontend/src/app/components/{list-generic-resource/list-location.component.ts => fhir-datatable/datatable-generic-resource/datatable-location.component.ts} (57%) rename frontend/src/app/components/{list-generic-resource/list-medication-administration.component.ts => fhir-datatable/datatable-generic-resource/datatable-medication-administration.component.ts} (64%) create mode 100644 frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-dispense.component.ts rename frontend/src/app/components/{list-generic-resource/list-medication-request.component.ts => fhir-datatable/datatable-generic-resource/datatable-medication-request.component.ts} (69%) create mode 100644 frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication.component.ts rename frontend/src/app/components/{list-generic-resource/list-nutrition-order.component.ts => fhir-datatable/datatable-generic-resource/datatable-nutrition-order.component.ts} (58%) rename frontend/src/app/components/{list-generic-resource/list-observation.component.ts => fhir-datatable/datatable-generic-resource/datatable-observation.component.ts} (63%) rename frontend/src/app/components/{list-generic-resource/list-organization.component.ts => fhir-datatable/datatable-generic-resource/datatable-organization.component.ts} (52%) rename frontend/src/app/components/{list-generic-resource/list-practitioner.component.ts => fhir-datatable/datatable-generic-resource/datatable-practitioner.component.ts} (53%) rename frontend/src/app/components/{list-generic-resource/list-procedure.component.ts => fhir-datatable/datatable-generic-resource/datatable-procedure.component.ts} (67%) rename frontend/src/app/components/{list-generic-resource/list-service-request.component.ts => fhir-datatable/datatable-generic-resource/datatable-service-request.component.ts} (73%) rename frontend/src/app/components/{list-generic-resource => fhir-datatable/datatable-generic-resource}/utils.ts (98%) create mode 100644 frontend/src/app/components/fhir-datatable/fhir-datatable.module.ts rename frontend/src/app/components/{resource-list/resource-list-outlet.directive.spec.ts => fhir-datatable/fhir-datatable/fhir-datatable-outlet.directive.spec.ts} (72%) rename frontend/src/app/components/{fhir/fhir-resource/fhir-resource-outlet.directive.ts => fhir-datatable/fhir-datatable/fhir-datatable-outlet.directive.ts} (63%) create mode 100644 frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.html rename frontend/src/app/components/{resource-list/resource-list.component.scss => fhir-datatable/fhir-datatable/fhir-datatable.component.scss} (100%) rename frontend/src/app/components/{resource-list/resource-list.component.spec.ts => fhir-datatable/fhir-datatable/fhir-datatable.component.spec.ts} (56%) create mode 100644 frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.ts rename frontend/src/app/components/{ => fhir-datatable}/list-patient/list-patient.component.html (100%) create mode 100644 frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.scss rename frontend/src/app/components/{ => fhir-datatable}/list-patient/list-patient.component.spec.ts (100%) rename frontend/src/app/components/{ => fhir-datatable}/list-patient/list-patient.component.ts (100%) delete mode 100644 frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.html delete mode 100644 frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.spec.ts delete mode 100644 frontend/src/app/components/list-generic-resource/list-adverse-event.component.ts delete mode 100644 frontend/src/app/components/list-generic-resource/list-coverage.component.ts delete mode 100644 frontend/src/app/components/list-generic-resource/list-explanation-of-benefit.component.ts delete mode 100644 frontend/src/app/components/list-generic-resource/list-fallback.component.ts delete mode 100644 frontend/src/app/components/list-generic-resource/list-generic-resource.component.html delete mode 100644 frontend/src/app/components/list-generic-resource/list-goal.component.ts delete mode 100644 frontend/src/app/components/list-generic-resource/list-medication-dispense.component.ts delete mode 100644 frontend/src/app/components/list-generic-resource/list-medication.component.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.html create mode 100644 frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.scss create mode 100644 frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.spec.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.stories.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.html create mode 100644 frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.scss create mode 100644 frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.spec.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.stories.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.html create mode 100644 frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.scss create mode 100644 frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.spec.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.stories.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.html create mode 100644 frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.scss create mode 100644 frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.spec.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.ts create mode 100644 frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.stories.ts create mode 100644 frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.html create mode 100644 frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.scss create mode 100644 frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.spec.ts create mode 100644 frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.ts create mode 100644 frontend/src/app/components/medical-record-wizard/medical-record-wizard.stories.ts create mode 100644 frontend/src/app/components/medical-record-wizard/medical-record-wizard.utilities.ts create mode 100644 frontend/src/app/components/nlm-typeahead/nlm-typeahead.stories.ts delete mode 100644 frontend/src/app/components/resource-list/resource-list.component.html delete mode 100644 frontend/src/app/components/resource-list/resource-list.component.ts delete mode 100644 frontend/src/app/pages/resource-creator/resource-creator.utilities.ts create mode 100644 frontend/src/app/pipes/human-name.pipe.spec.ts create mode 100644 frontend/src/app/pipes/human-name.pipe.ts create mode 100644 frontend/src/app/pipes/reference-uri.pipe.spec.ts create mode 100644 frontend/src/app/pipes/reference-uri.pipe.ts create mode 100644 frontend/src/assets/sources/fasten.png create mode 100644 frontend/src/lib/fixtures/r4/resources/binary/exampleMarkdown.json create mode 100644 frontend/src/lib/fixtures/r4/resources/binary/exampleRtf.json create mode 100644 frontend/src/lib/utils/bundle_references.ts diff --git a/backend/pkg/database/gorm_common.go b/backend/pkg/database/gorm_common.go index 0c6d9ca1..e6d7a8d1 100644 --- a/backend/pkg/database/gorm_common.go +++ b/backend/pkg/database/gorm_common.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + sourcePkg "github.com/fastenhealth/fasten-sources/pkg" "strings" "time" @@ -39,6 +40,7 @@ func (gr *GormRepository) Close() error { // User //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// func (gr *GormRepository) CreateUser(ctx context.Context, user *models.User) error { if err := user.HashPassword(user.Password); err != nil { return err @@ -53,6 +55,17 @@ func (gr *GormRepository) CreateUser(ctx context.Context, user *models.User) err if err != nil { return err } + + //create Fasten source credential for this user. + fastenUserCred := models.SourceCredential{ + UserID: user.ID, + SourceType: sourcePkg.SourceTypeFasten, + } + fastenUserCredResp := gr.GormClient.Create(&fastenUserCred) + if fastenUserCredResp.Error != nil { + return fastenUserCredResp.Error + } + return nil } func (gr *GormRepository) GetUserByUsername(ctx context.Context, username string) (*models.User, error) { @@ -94,10 +107,13 @@ func (gr *GormRepository) GetCurrentUser(ctx context.Context) (*models.User, err return ¤tUser, nil } +// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Glossary //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// func (gr *GormRepository) CreateGlossaryEntry(ctx context.Context, glossaryEntry *models.Glossary) error { record := gr.GormClient.WithContext(ctx).Create(glossaryEntry) if record.Error != nil { @@ -114,6 +130,8 @@ func (gr *GormRepository) GetGlossaryEntry(ctx context.Context, code string, cod return &foundGlossaryEntry, result.Error } +// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Summary //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -180,6 +198,8 @@ func (gr *GormRepository) GetSummary(ctx context.Context) (*models.Summary, erro // Resource //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + // This function will create a new resource if it does not exist, or update an existing resource if it does exist. // It will also create associations between fhir resources // This function is called directly by fasten-sources @@ -210,30 +230,54 @@ func (gr *GormRepository) UpsertRawResource(ctx context.Context, sourceCredentia //note: these associations are not reciprocal, (i.e. if Procedure references Location, Location may not reference Procedure) if rawResource.ReferencedResources != nil && len(rawResource.ReferencedResources) > 0 { for _, referencedResource := range rawResource.ReferencedResources { - parts := strings.Split(referencedResource, "/") - if len(parts) != 2 { - continue - } - relatedResource := &models.ResourceBase{ - OriginBase: models.OriginBase{ - SourceID: source.ID, - SourceResourceType: parts[0], - SourceResourceID: parts[1], - }, - RelatedResource: nil, - } - err := gr.AddResourceAssociation( - ctx, - source, - wrappedResourceModel.SourceResourceType, - wrappedResourceModel.SourceResourceID, - source, - relatedResource.SourceResourceType, - relatedResource.SourceResourceID, - ) - if err != nil { - return false, err + var relatedResource *models.ResourceBase + + if strings.HasPrefix(referencedResource, sourcePkg.FASTENHEALTH_URN_PREFIX) { + gr.Logger.Infof("parsing external urn:fastenhealth-fhir reference: %v", referencedResource) + + targetSourceId, targetResourceType, targetResourceId, err := sourcePkg.ParseReferenceUri(&referencedResource) + if err != nil { + gr.Logger.Warnf("could not parse urn:fastenhealth-fhir reference: %v", referencedResource) + continue + } + err = gr.UpsertRawResourceAssociation( + ctx, + source.ID.String(), + wrappedResourceModel.SourceResourceType, + wrappedResourceModel.SourceResourceID, + targetSourceId, + targetResourceType, + targetResourceId, + ) + if err != nil { + return false, err + } + } else { + parts := strings.Split(referencedResource, "/") + if len(parts) != 2 { + continue + } + relatedResource = &models.ResourceBase{ + OriginBase: models.OriginBase{ + SourceID: source.ID, + SourceResourceType: parts[0], + SourceResourceID: parts[1], + }, + RelatedResource: nil, + } + err := gr.AddResourceAssociation( + ctx, + source, + wrappedResourceModel.SourceResourceType, + wrappedResourceModel.SourceResourceID, + source, + relatedResource.SourceResourceType, + relatedResource.SourceResourceID, + ) + if err != nil { + return false, err + } } } } @@ -242,6 +286,44 @@ func (gr *GormRepository) UpsertRawResource(ctx context.Context, sourceCredentia } +func (gr *GormRepository) UpsertRawResourceAssociation( + ctx context.Context, + sourceId string, + sourceResourceType string, + sourceResourceId string, + targetSourceId string, + targetResourceType string, + targetResourceId string, +) error { + + if sourceId == targetSourceId && sourceResourceType == targetResourceType && sourceResourceId == targetResourceId { + gr.Logger.Warnf("cannot create self-referential association, ignoring") + return nil + } + var sourceCredential *models.SourceCredential + var targetSourceCredential *models.SourceCredential + var err error + if sourceId == targetSourceId { + sourceCredential, err = gr.GetSource(ctx, sourceId) + if err != nil { + return err + } + targetSourceCredential = sourceCredential + } else { + sourceCredential, err = gr.GetSource(ctx, sourceId) + if err != nil { + return err + } + targetSourceCredential, err = gr.GetSource(ctx, targetSourceId) + if err != nil { + return err + } + } + + //SECURITY: sourceCredential and targetSourceCredential are guaranteed to be owned by the same user, and will be confirmed within the addAssociation function + return gr.AddResourceAssociation(ctx, sourceCredential, sourceResourceType, sourceResourceId, targetSourceCredential, targetResourceType, targetResourceId) +} + // UpsertResource // this method will upsert a resource, however it will not create associations. // UPSERT operation @@ -448,10 +530,14 @@ func (gr *GormRepository) GetPatientForSources(ctx context.Context) ([]models.Re return wrappedResourceModels, results.Error } +// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Resource Associations //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + // verifyAssociationPermission ensure that the sources are "owned" by the same user, and that the user is the current user func (gr *GormRepository) verifyAssociationPermission(ctx context.Context, sourceUserID uuid.UUID, relatedSourceUserID uuid.UUID) error { currentUser, currentUserErr := gr.GetCurrentUser(ctx) @@ -542,11 +628,14 @@ func (gr *GormRepository) FindResourceAssociationsByTypeAndId(ctx context.Contex ResourceBaseSourceID: source.ID, ResourceBaseSourceResourceType: resourceType, ResourceBaseSourceResourceID: resourceId, + RelatedResourceUserID: currentUser.ID, }). Find(&relatedResources) return relatedResources, result.Error } +// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Resource Composition (Grouping) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -565,6 +654,8 @@ func (gr *GormRepository) FindResourceAssociationsByTypeAndId(ctx context.Contex // - add AddResourceAssociation for all resources linked to the Composition resource // - store the Composition resource // TODO: determine if we should be using a List Resource instead of a Composition resource +// +// Deprecated: This method has been deprecated. It has been replaced in favor of Fasten SourceCredential & associations func (gr *GormRepository) AddResourceComposition(ctx context.Context, compositionTitle string, resources []*models.ResourceBase) error { currentUser, currentUserErr := gr.GetCurrentUser(ctx) if currentUserErr != nil { @@ -718,6 +809,8 @@ func (gr *GormRepository) AddResourceComposition(ctx context.Context, compositio // SourceCredential //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// + func (gr *GormRepository) CreateSource(ctx context.Context, sourceCreds *models.SourceCredential) error { currentUser, currentUserErr := gr.GetCurrentUser(ctx) if currentUserErr != nil { @@ -846,10 +939,10 @@ func (gr *GormRepository) GetSourceSummary(ctx context.Context, sourceId string) Table(patientTableName). First(&wrappedPatientResourceModel) - if patientResults.Error != nil { - return nil, patientResults.Error + //some sources may not have a patient resource (including the Fasten source) + if patientResults.Error == nil { + sourceSummary.Patient = &wrappedPatientResourceModel } - sourceSummary.Patient = &wrappedPatientResourceModel return sourceSummary, nil } @@ -925,10 +1018,13 @@ func (gr *GormRepository) DeleteSource(ctx context.Context, sourceId string) (in return rowsEffected, results.Error } +// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Background Job //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// func (gr *GormRepository) CreateBackgroundJob(ctx context.Context, backgroundJob *models.BackgroundJob) error { currentUser, currentUserErr := gr.GetCurrentUser(ctx) if currentUserErr != nil { @@ -1117,6 +1213,8 @@ func (gr *GormRepository) CancelAllLockedBackgroundJobsAndFail() error { } +// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Utilities //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/backend/pkg/database/gorm_repository_migrations.go b/backend/pkg/database/gorm_repository_migrations.go index affdcc7b..c957f283 100644 --- a/backend/pkg/database/gorm_repository_migrations.go +++ b/backend/pkg/database/gorm_repository_migrations.go @@ -1,8 +1,11 @@ package database import ( + "context" + "fmt" "github.com/fastenhealth/fasten-onprem/backend/pkg/models" databaseModel "github.com/fastenhealth/fasten-onprem/backend/pkg/models/database" + sourcePkg "github.com/fastenhealth/fasten-sources/pkg" "github.com/go-gormigrate/gormigrate/v2" "gorm.io/gorm" ) @@ -14,7 +17,7 @@ func (gr *GormRepository) Migrate() error { gormMigrateOptions := gormigrate.DefaultOptions gormMigrateOptions.UseTransaction = true - //use echo $(date '+%Y%m%d%H%M%S') to generate new ID's + //use "echo $(date '+%Y%m%d%H%M%S')" to generate new ID's m := gormigrate.New(gr.GormClient, gormMigrateOptions, []*gormigrate.Migration{ { ID: "20231017112246", // base database models //TODO: figure out how to version these correctly (SourceCredential is complicated) @@ -37,6 +40,31 @@ func (gr *GormRepository) Migrate() error { return databaseModel.Migrate(tx) }, }, + { + ID: "20231201122541", // Adding Fasten Source Credential for each user + Migrate: func(tx *gorm.DB) error { + + users := []models.User{} + results := tx.Find(&users) + if results.Error != nil { + return results.Error + } + for _, user := range users { + tx.Logger.Info(context.Background(), fmt.Sprintf("Creating Fasten Source Credential for user: %s", user.ID)) + + fastenUserCred := models.SourceCredential{ + UserID: user.ID, + SourceType: sourcePkg.SourceTypeFasten, + } + fastenUserCredCreateResp := tx.Create(&fastenUserCred) + if fastenUserCredCreateResp.Error != nil { + tx.Logger.Error(context.Background(), fmt.Sprintf("An error occurred creating Fasten Source Credential for user: %s", user.ID)) + return fastenUserCredCreateResp.Error + } + } + return nil + }, + }, }) if err := m.Migrate(); err != nil { diff --git a/backend/pkg/database/gorm_repository_test.go b/backend/pkg/database/gorm_repository_test.go index deb177ba..ec257f56 100644 --- a/backend/pkg/database/gorm_repository_test.go +++ b/backend/pkg/database/gorm_repository_test.go @@ -1021,7 +1021,7 @@ func (suite *RepositoryTestSuite) TestGetSummary() { {"count": int64(16), "resource_type": "Procedure"}, }, sourceSummary.ResourceTypeCounts) - require.Equal(suite.T(), 2, len(sourceSummary.Sources)) + require.Equal(suite.T(), 3, len(sourceSummary.Sources)) require.Equal(suite.T(), 2, len(sourceSummary.Patients)) } diff --git a/backend/pkg/database/interface.go b/backend/pkg/database/interface.go index c061ff06..a5ec4f2d 100644 --- a/backend/pkg/database/interface.go +++ b/backend/pkg/database/interface.go @@ -29,6 +29,8 @@ type DatabaseRepository interface { RemoveResourceAssociation(ctx context.Context, source *models.SourceCredential, resourceType string, resourceId string, relatedSource *models.SourceCredential, relatedResourceType string, relatedResourceId string) error FindResourceAssociationsByTypeAndId(ctx context.Context, source *models.SourceCredential, resourceType string, resourceId string) ([]models.RelatedResource, error) GetFlattenedResourceGraph(ctx context.Context, graphType pkg.ResourceGraphType, options models.ResourceGraphOptions) (map[string][]*models.ResourceBase, error) + + // Deprecated:This method has been deprecated. It has been replaced in favor of Fasten SourceCredential & associations AddResourceComposition(ctx context.Context, compositionTitle string, resources []*models.ResourceBase) error //UpsertProfile(context.Context, *models.Profile) error //UpsertOrganziation(context.Context, *models.Organization) error @@ -55,6 +57,15 @@ type DatabaseRepository interface { PopulateDefaultUserSettings(ctx context.Context, userId uuid.UUID) error //used by fasten-sources Clients - UpsertRawResource(ctx context.Context, sourceCredentials sourcePkg.SourceCredential, rawResource sourcePkg.RawResourceFhir) (bool, error) BackgroundJobCheckpoint(ctx context.Context, checkpointData map[string]interface{}, errorData map[string]interface{}) + UpsertRawResource(ctx context.Context, sourceCredentials sourcePkg.SourceCredential, rawResource sourcePkg.RawResourceFhir) (bool, error) + UpsertRawResourceAssociation( + ctx context.Context, + sourceId string, + sourceResourceType string, + sourceResourceId string, + targetSourceId string, + targetResourceType string, + targetResourceId string, + ) error } diff --git a/backend/pkg/models/source_credential.go b/backend/pkg/models/source_credential.go index 9b0b3341..888ea07e 100644 --- a/backend/pkg/models/source_credential.go +++ b/backend/pkg/models/source_credential.go @@ -69,6 +69,9 @@ type SourceCredential struct { func (s *SourceCredential) GetSourceType() sourcesPkg.SourceType { return s.SourceType } +func (s *SourceCredential) GetSourceId() string { + return s.ID.String() +} func (s *SourceCredential) GetClientId() string { return s.ClientId diff --git a/backend/pkg/web/handler/background_jobs.go b/backend/pkg/web/handler/background_jobs.go index cd3d2568..1e3a07fc 100644 --- a/backend/pkg/web/handler/background_jobs.go +++ b/backend/pkg/web/handler/background_jobs.go @@ -17,14 +17,65 @@ import ( "time" ) -// BackgroundJobSyncResources is a background job that syncs all FHIR resource for a given source +// This function is used to sync resources from a source (via a callback function). The BackgroundJobSyncResourcesWrapper contains the logic for registering the background job tracking the sync. +func BackgroundJobSyncResources( + parentContext context.Context, + logger *logrus.Entry, + databaseRepo database.DatabaseRepository, + sourceCred *models.SourceCredential, +) (sourceModels.UpsertSummary, error) { + return BackgroundJobSyncResourcesWrapper( + parentContext, + logger, + databaseRepo, + sourceCred, + func( + _backgroundJobContext context.Context, + _logger *logrus.Entry, + _databaseRepo database.DatabaseRepository, + _sourceCred *models.SourceCredential, + ) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) { + // after creating the client, we should do a bulk import + sourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), _sourceCred.SourceType, _backgroundJobContext, _logger, _sourceCred) + if err != nil { + resultErr := fmt.Errorf("an error occurred while initializing hub client using source credential: %w", err) + _logger.Errorln(resultErr) + return nil, sourceModels.UpsertSummary{}, resultErr + } + + summary, err := sourceClient.SyncAll(_databaseRepo) + if err != nil { + resultErr := fmt.Errorf("an error occurred while bulk importing resources from source: %w", err) + _logger.Errorln(resultErr) + return sourceClient, summary, resultErr + } + return sourceClient, summary, nil + }) +} + +// BackgroundJobSyncResourcesWrapper is a background job that syncs all FHIR resource for a given source // It is a blocking function that will return only when the sync is complete or has failed // It will create a background job and associate it with the source // It will also update the access token and refresh token if they have been updated // It will return the sync summary and error if any +// +// It's a wrapper function that takes a callback function as an argument. +// The callback function is the actual sync operation that will be run in the background (regular source or manual source) +// // TODO: run in background thread, or use https://gobyexample.com/tickers // TODO: use goroutine to truely run in the background (how will that work with DatabaseRepository, is that thread safe?) Mutex needed? -func BackgroundJobSyncResources(parentContext context.Context, logger *logrus.Entry, databaseRepo database.DatabaseRepository, sourceCred *models.SourceCredential) (sourceModels.UpsertSummary, error) { +func BackgroundJobSyncResourcesWrapper( + parentContext context.Context, + logger *logrus.Entry, + databaseRepo database.DatabaseRepository, + sourceCred *models.SourceCredential, + callbackFn func( + _backgroundJobContext context.Context, + _logger *logrus.Entry, + _databaseRepo database.DatabaseRepository, + _sourceCred *models.SourceCredential, + ) (sourceModels.SourceClient, sourceModels.UpsertSummary, error), +) (sourceModels.UpsertSummary, error) { var resultErr error var backgroundJob *models.BackgroundJob @@ -54,14 +105,6 @@ func BackgroundJobSyncResources(parentContext context.Context, logger *logrus.En //we can safely ignore this error, because we'll be updating the status of the background job again later } - // after creating the client, we should do a bulk import - sourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), sourceCred.SourceType, backgroundJobContext, logger, sourceCred) - if err != nil { - resultErr = fmt.Errorf("an error occurred while initializing hub client using source credential: %w", err) - logger.Errorln(resultErr) - return sourceModels.UpsertSummary{}, resultErr - } - // BEGIN FINALIZER defer func() { //finalizer function - update the sync status to completed (or failed depending on the error status) @@ -124,10 +167,11 @@ func BackgroundJobSyncResources(parentContext context.Context, logger *logrus.En }() // END FINALIZER - summary, err := sourceClient.SyncAll(databaseRepo) - if err != nil { - resultErr = fmt.Errorf("an error occurred while bulk importing resources from source: %w", err) - logger.Errorln(resultErr) + var sourceClient sourceModels.SourceClient + var summary sourceModels.UpsertSummary + sourceClient, summary, resultErr = callbackFn(backgroundJobContext, logger, databaseRepo, sourceCred) + if resultErr != nil { + logger.Errorln("An error occurred while syncing resources, ignoring", resultErr) return summary, resultErr } diff --git a/backend/pkg/web/handler/resource_fhir.go b/backend/pkg/web/handler/resource_fhir.go index 534220db..aed4cd88 100644 --- a/backend/pkg/web/handler/resource_fhir.go +++ b/backend/pkg/web/handler/resource_fhir.go @@ -94,6 +94,7 @@ func GetResourceFhir(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true, "data": wrappedResourceModel}) } +// deprecated - using Manual Resource Wizard instead func CreateResourceComposition(c *gin.Context) { logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry) diff --git a/backend/pkg/web/handler/resource_related.go b/backend/pkg/web/handler/resource_related.go new file mode 100644 index 00000000..cf3eaf41 --- /dev/null +++ b/backend/pkg/web/handler/resource_related.go @@ -0,0 +1,95 @@ +package handler + +import ( + "context" + "fmt" + "github.com/fastenhealth/fasten-onprem/backend/pkg" + "github.com/fastenhealth/fasten-onprem/backend/pkg/database" + "github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus" + "github.com/fastenhealth/fasten-onprem/backend/pkg/models" + "github.com/fastenhealth/fasten-sources/clients/factory" + sourceModels "github.com/fastenhealth/fasten-sources/clients/models" + sourcePkg "github.com/fastenhealth/fasten-sources/pkg" + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "net/http" +) + +// mimics functionality in CreateManualSource +func CreateRelatedResources(c *gin.Context) { + logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry) + databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository) + eventBus := c.MustGet(pkg.ContextKeyTypeEventBusServer).(event_bus.Interface) + + // store the bundle file locally + bundleFile, err := storeFileLocally(c) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()}) + return + } + + //step 2: find a reference to the Fasten source for this user + sourceCredentials, err := databaseRepo.GetSources(c) + var fastenSourceCredential *models.SourceCredential + for _, sourceCredential := range sourceCredentials { + if sourceCredential.SourceType == sourcePkg.SourceTypeFasten { + fastenSourceCredential = &sourceCredential + break + } + } + if fastenSourceCredential == nil { + c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "could not find Fasten source for this user"}) + return + } + + summary, err := BackgroundJobSyncResourcesWrapper( + c, + logger, + databaseRepo, + fastenSourceCredential, + func( + _backgroundJobContext context.Context, + _logger *logrus.Entry, + _databaseRepo database.DatabaseRepository, + _sourceCred *models.SourceCredential, + ) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) { + + //step 3: create a "fasten" client, which we can use to parse resources to add to the database + fastenSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), sourcePkg.SourceTypeFasten, _backgroundJobContext, _logger, _sourceCred) + if err != nil { + resultErr := fmt.Errorf("could not create Fasten source client") + _logger.Errorln(resultErr) + return fastenSourceClient, sourceModels.UpsertSummary{}, resultErr + } + + //step 4: parse the resources from the bundle + summary, err := fastenSourceClient.SyncAllBundle(_databaseRepo, bundleFile, sourcePkg.FhirVersion401) + if err != nil { + resultErr := fmt.Errorf("an error occurred while processing bundle: %v", err) + _logger.Errorln(resultErr) + return fastenSourceClient, summary, resultErr + } + return fastenSourceClient, summary, nil + }) + + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()}) + return + } + + //step 7 notify the event bus of the new resources + currentUser, _ := databaseRepo.GetCurrentUser(c) + err = eventBus.PublishMessage( + models.NewEventSourceComplete( + currentUser.ID.String(), + fastenSourceCredential.ID.String(), + ), + ) + + if err != nil { + logger.Warnf("ignoring: an error occurred while publishing sync complete event: %v", err) + } + + c.JSON(http.StatusOK, gin.H{"success": true, "data": summary, "source": fastenSourceCredential}) + +} diff --git a/backend/pkg/web/handler/source.go b/backend/pkg/web/handler/source.go index 414d3454..04d619ef 100644 --- a/backend/pkg/web/handler/source.go +++ b/backend/pkg/web/handler/source.go @@ -1,18 +1,21 @@ package handler import ( + "context" "fmt" "github.com/fastenhealth/fasten-onprem/backend/pkg" "github.com/fastenhealth/fasten-onprem/backend/pkg/database" "github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus" "github.com/fastenhealth/fasten-onprem/backend/pkg/models" "github.com/fastenhealth/fasten-sources/clients/factory" + sourceModels "github.com/fastenhealth/fasten-sources/clients/models" sourcePkg "github.com/fastenhealth/fasten-sources/pkg" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/sirupsen/logrus" "io/ioutil" "net/http" + "os" ) func CreateReconnectSource(c *gin.Context) { @@ -122,30 +125,17 @@ func SourceSync(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true, "source": sourceCred, "data": summary}) } +// mimics functionality in CreateRelatedResources +// mimics functionality in SourceSync func CreateManualSource(c *gin.Context) { logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry) databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository) eventBus := c.MustGet(pkg.ContextKeyTypeEventBusServer).(event_bus.Interface) - // single file - file, err := c.FormFile("file") + // store the bundle file locally + bundleFile, err := storeFileLocally(c) 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"}) + c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()}) return } @@ -178,23 +168,40 @@ func CreateManualSource(c *gin.Context) { return } - manualSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), sourcePkg.SourceTypeManual, c, logger, &manualSourceCredential) - if err != nil { - logger.Errorln("An error occurred while initializing hub client using manual source with credential", err) - c.JSON(http.StatusInternalServerError, gin.H{"success": false}) - return - } + summary, err := BackgroundJobSyncResourcesWrapper( + c, + logger, + databaseRepo, + &manualSourceCredential, + func( + _backgroundJobContext context.Context, + _logger *logrus.Entry, + _databaseRepo database.DatabaseRepository, + _sourceCred *models.SourceCredential, + ) (sourceModels.SourceClient, sourceModels.UpsertSummary, error) { + manualSourceClient, err := factory.GetSourceClient(sourcePkg.GetFastenLighthouseEnv(), sourcePkg.SourceTypeManual, _backgroundJobContext, _logger, _sourceCred) + if err != nil { + resultErr := fmt.Errorf("an error occurred while initializing hub client using manual source with credential: %w", err) + logger.Errorln(resultErr) + return manualSourceClient, sourceModels.UpsertSummary{}, resultErr + } + + summary, err := manualSourceClient.SyncAllBundle(_databaseRepo, bundleFile, bundleType) + if err != nil { + resultErr := fmt.Errorf("an error occurred while processing bundle: %w", err) + logger.Errorln(resultErr) + return manualSourceClient, sourceModels.UpsertSummary{}, resultErr + } + return manualSourceClient, summary, nil + }) - summary, err := manualSourceClient.SyncAllBundle(databaseRepo, bundleFile, bundleType) if err != nil { - logger.Errorln("An error occurred while processing bundle", err) c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()}) return } //publish event currentUser, _ := databaseRepo.GetCurrentUser(c) - err = eventBus.PublishMessage( models.NewEventSourceComplete( currentUser.ID.String(), @@ -260,3 +267,29 @@ func DeleteSource(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{"success": true, "data": rowsEffected}) } + +// Helpers +func storeFileLocally(c *gin.Context) (*os.File, error) { + // 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 nil, fmt.Errorf("could not extract file from form") + } + 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 nil, fmt.Errorf("could not create temp file") + } + + // 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 nil, fmt.Errorf("could not save temp file") + } + return bundleFile, nil +} diff --git a/backend/pkg/web/server.go b/backend/pkg/web/server.go index a8171b24..d5b2c58b 100644 --- a/backend/pkg/web/server.go +++ b/backend/pkg/web/server.go @@ -74,7 +74,9 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) { secure.GET("/resource/fhir", handler.ListResourceFhir) secure.POST("/resource/graph/:graphType", handler.GetResourceFhirGraph) secure.GET("/resource/fhir/:sourceId/:resourceId", handler.GetResourceFhir) + secure.POST("/resource/composition", handler.CreateResourceComposition) + secure.POST("/resource/related", handler.CreateRelatedResources) secure.GET("/dashboards", handler.GetDashboard) secure.POST("/dashboards", handler.AddDashboardLocation) diff --git a/frontend/package.json b/frontend/package.json index 22153a64..9a22fe62 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -51,6 +51,7 @@ "ngx-highlightjs": "^7.0.1", "ngx-infinite-scroll": "^14.0.0", "ngx-moment": "^6.0.2", + "parse-full-name": "^1.2.6", "rxjs": "~6.5.4", "tslib": "^2.0.0", "uuid": "^9.0.0", diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index d914c927..33326881 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import {NavigationEnd, Router} from '@angular/router'; import {Observable, of} from 'rxjs'; import {ToastService} from './services/toast.service'; +import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'app-root', @@ -15,7 +16,11 @@ export class AppComponent implements OnInit { showHeader:boolean = false; showFooter:boolean = true; - constructor(private router: Router, private toastService: ToastService) {} + constructor( + private router: Router, + private toastService: ToastService, + private modalService: NgbModal + ) {} ngOnInit() { @@ -25,17 +30,21 @@ export class AppComponent implements OnInit { document.querySelector('body').appendChild(navbarBackdrop); //determine if we should show the header - this.router.events.subscribe(event => this.modifyHeader(event)); + this.router.events.subscribe(event => this.routerEvent(event)); } - modifyHeader(event) { + routerEvent(event) { if (event instanceof NavigationEnd) { + //modify header if (event.url?.startsWith('/auth') || event.url?.startsWith('/desktop')) { this.showHeader = false; } else { // console.log("NU") this.showHeader = true; } + + // close all open modals when route change + this.modalService.dismissAll(); } } } diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index e19edd2e..1dec0827 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -38,6 +38,8 @@ import { ExploreComponent } from './pages/explore/explore.component'; import {DirectivesModule} from './directives/directives.module'; import { DesktopCallbackComponent } from './pages/desktop-callback/desktop-callback.component'; import { BackgroundJobsComponent } from './pages/background-jobs/background-jobs.component'; +import {FhirCardModule} from './components/fhir-card/fhir-card.module'; +import {FhirDatatableModule} from './components/fhir-datatable/fhir-datatable.module'; @NgModule({ declarations: [ @@ -64,6 +66,8 @@ import { BackgroundJobsComponent } from './pages/background-jobs/background-jobs BrowserModule, FontAwesomeModule, SharedModule, + FhirCardModule, + FhirDatatableModule, AppRoutingModule, HttpClientModule, NgbModule, diff --git a/frontend/src/app/components/fhir/common/badge/badge.component.html b/frontend/src/app/components/fhir-card/common/badge/badge.component.html similarity index 100% rename from frontend/src/app/components/fhir/common/badge/badge.component.html rename to frontend/src/app/components/fhir-card/common/badge/badge.component.html diff --git a/frontend/src/app/components/fhir/common/badge/badge.component.scss b/frontend/src/app/components/fhir-card/common/badge/badge.component.scss similarity index 100% rename from frontend/src/app/components/fhir/common/badge/badge.component.scss rename to frontend/src/app/components/fhir-card/common/badge/badge.component.scss diff --git a/frontend/src/app/components/fhir/common/badge/badge.component.spec.ts b/frontend/src/app/components/fhir-card/common/badge/badge.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/common/badge/badge.component.spec.ts rename to frontend/src/app/components/fhir-card/common/badge/badge.component.spec.ts diff --git a/frontend/src/app/components/fhir/common/badge/badge.component.ts b/frontend/src/app/components/fhir-card/common/badge/badge.component.ts similarity index 100% rename from frontend/src/app/components/fhir/common/badge/badge.component.ts rename to frontend/src/app/components/fhir-card/common/badge/badge.component.ts diff --git a/frontend/src/app/components/fhir/common/badge/badge.stories.ts b/frontend/src/app/components/fhir-card/common/badge/badge.stories.ts similarity index 96% rename from frontend/src/app/components/fhir/common/badge/badge.stories.ts rename to frontend/src/app/components/fhir-card/common/badge/badge.stories.ts index 7f453bfe..ce329367 100644 --- a/frontend/src/app/components/fhir/common/badge/badge.stories.ts +++ b/frontend/src/app/components/fhir-card/common/badge/badge.stories.ts @@ -3,7 +3,7 @@ import {BadgeComponent} from "./badge.component"; // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Common/Badge', + title: 'Fhir Card/Common/Badge', component: BadgeComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/common/table/table-row-item.spec.ts b/frontend/src/app/components/fhir-card/common/table/table-row-item.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/common/table/table-row-item.spec.ts rename to frontend/src/app/components/fhir-card/common/table/table-row-item.spec.ts diff --git a/frontend/src/app/components/fhir/common/table/table-row-item.ts b/frontend/src/app/components/fhir-card/common/table/table-row-item.ts similarity index 100% rename from frontend/src/app/components/fhir/common/table/table-row-item.ts rename to frontend/src/app/components/fhir-card/common/table/table-row-item.ts diff --git a/frontend/src/app/components/fhir/common/table/table.component.html b/frontend/src/app/components/fhir-card/common/table/table.component.html similarity index 100% rename from frontend/src/app/components/fhir/common/table/table.component.html rename to frontend/src/app/components/fhir-card/common/table/table.component.html diff --git a/frontend/src/app/components/fhir/common/table/table.component.scss b/frontend/src/app/components/fhir-card/common/table/table.component.scss similarity index 100% rename from frontend/src/app/components/fhir/common/table/table.component.scss rename to frontend/src/app/components/fhir-card/common/table/table.component.scss diff --git a/frontend/src/app/components/fhir/common/table/table.component.spec.ts b/frontend/src/app/components/fhir-card/common/table/table.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/common/table/table.component.spec.ts rename to frontend/src/app/components/fhir-card/common/table/table.component.spec.ts diff --git a/frontend/src/app/components/fhir/common/table/table.component.ts b/frontend/src/app/components/fhir-card/common/table/table.component.ts similarity index 100% rename from frontend/src/app/components/fhir/common/table/table.component.ts rename to frontend/src/app/components/fhir-card/common/table/table.component.ts diff --git a/frontend/src/app/components/fhir/common/table/table.stories.ts b/frontend/src/app/components/fhir-card/common/table/table.stories.ts similarity index 98% rename from frontend/src/app/components/fhir/common/table/table.stories.ts rename to frontend/src/app/components/fhir-card/common/table/table.stories.ts index 16988168..7a616fff 100644 --- a/frontend/src/app/components/fhir/common/table/table.stories.ts +++ b/frontend/src/app/components/fhir-card/common/table/table.stories.ts @@ -5,7 +5,7 @@ import {FastenDisplayModel} from "../../../../../lib/models/fasten/fasten-displa // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Common/Table', + title: 'Fhir Card/Common/Table', component: TableComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/datatypes/binary-text/binary-text.component.html b/frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.component.html similarity index 100% rename from frontend/src/app/components/fhir/datatypes/binary-text/binary-text.component.html rename to frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.component.html diff --git a/frontend/src/app/components/fhir/datatypes/binary-text/binary-text.component.scss b/frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.component.scss similarity index 100% rename from frontend/src/app/components/fhir/datatypes/binary-text/binary-text.component.scss rename to frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.component.scss diff --git a/frontend/src/app/components/fhir/datatypes/binary-text/binary-text.component.spec.ts b/frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/binary-text/binary-text.component.spec.ts rename to frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.component.spec.ts diff --git a/frontend/src/app/components/fhir/datatypes/binary-text/binary-text.component.ts b/frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.component.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/binary-text/binary-text.component.ts rename to frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.component.ts diff --git a/frontend/src/app/components/fhir/datatypes/binary-text/binary-text.stories.ts b/frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.stories.ts similarity index 96% rename from frontend/src/app/components/fhir/datatypes/binary-text/binary-text.stories.ts rename to frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.stories.ts index eab09dfc..54d30f13 100644 --- a/frontend/src/app/components/fhir/datatypes/binary-text/binary-text.stories.ts +++ b/frontend/src/app/components/fhir-card/datatypes/binary-text/binary-text.stories.ts @@ -8,7 +8,7 @@ import {BinaryModel} from "../../../../../lib/models/resources/binary-model"; // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Datatypes/BinaryText', + title: 'Fhir Card/Datatypes/BinaryText', component: BinaryTextComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/datatypes/codable-concept/codable-concept.component.html b/frontend/src/app/components/fhir-card/datatypes/codable-concept/codable-concept.component.html similarity index 100% rename from frontend/src/app/components/fhir/datatypes/codable-concept/codable-concept.component.html rename to frontend/src/app/components/fhir-card/datatypes/codable-concept/codable-concept.component.html diff --git a/frontend/src/app/components/fhir/datatypes/codable-concept/codable-concept.component.scss b/frontend/src/app/components/fhir-card/datatypes/codable-concept/codable-concept.component.scss similarity index 100% rename from frontend/src/app/components/fhir/datatypes/codable-concept/codable-concept.component.scss rename to frontend/src/app/components/fhir-card/datatypes/codable-concept/codable-concept.component.scss diff --git a/frontend/src/app/components/fhir/datatypes/codable-concept/codable-concept.component.spec.ts b/frontend/src/app/components/fhir-card/datatypes/codable-concept/codable-concept.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/codable-concept/codable-concept.component.spec.ts rename to frontend/src/app/components/fhir-card/datatypes/codable-concept/codable-concept.component.spec.ts diff --git a/frontend/src/app/components/fhir/datatypes/codable-concept/codable-concept.component.ts b/frontend/src/app/components/fhir-card/datatypes/codable-concept/codable-concept.component.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/codable-concept/codable-concept.component.ts rename to frontend/src/app/components/fhir-card/datatypes/codable-concept/codable-concept.component.ts diff --git a/frontend/src/app/components/fhir/datatypes/coding/coding.component.html b/frontend/src/app/components/fhir-card/datatypes/coding/coding.component.html similarity index 100% rename from frontend/src/app/components/fhir/datatypes/coding/coding.component.html rename to frontend/src/app/components/fhir-card/datatypes/coding/coding.component.html diff --git a/frontend/src/app/components/fhir/datatypes/coding/coding.component.scss b/frontend/src/app/components/fhir-card/datatypes/coding/coding.component.scss similarity index 100% rename from frontend/src/app/components/fhir/datatypes/coding/coding.component.scss rename to frontend/src/app/components/fhir-card/datatypes/coding/coding.component.scss diff --git a/frontend/src/app/components/fhir/datatypes/coding/coding.component.spec.ts b/frontend/src/app/components/fhir-card/datatypes/coding/coding.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/coding/coding.component.spec.ts rename to frontend/src/app/components/fhir-card/datatypes/coding/coding.component.spec.ts diff --git a/frontend/src/app/components/fhir/datatypes/coding/coding.component.ts b/frontend/src/app/components/fhir-card/datatypes/coding/coding.component.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/coding/coding.component.ts rename to frontend/src/app/components/fhir-card/datatypes/coding/coding.component.ts diff --git a/frontend/src/app/components/fhir/datatypes/dicom/dicom.component.html b/frontend/src/app/components/fhir-card/datatypes/dicom/dicom.component.html similarity index 100% rename from frontend/src/app/components/fhir/datatypes/dicom/dicom.component.html rename to frontend/src/app/components/fhir-card/datatypes/dicom/dicom.component.html diff --git a/frontend/src/app/components/fhir/datatypes/dicom/dicom.component.scss b/frontend/src/app/components/fhir-card/datatypes/dicom/dicom.component.scss similarity index 100% rename from frontend/src/app/components/fhir/datatypes/dicom/dicom.component.scss rename to frontend/src/app/components/fhir-card/datatypes/dicom/dicom.component.scss diff --git a/frontend/src/app/components/fhir/datatypes/dicom/dicom.component.spec.ts b/frontend/src/app/components/fhir-card/datatypes/dicom/dicom.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/dicom/dicom.component.spec.ts rename to frontend/src/app/components/fhir-card/datatypes/dicom/dicom.component.spec.ts diff --git a/frontend/src/app/components/fhir/datatypes/dicom/dicom.component.ts b/frontend/src/app/components/fhir-card/datatypes/dicom/dicom.component.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/dicom/dicom.component.ts rename to frontend/src/app/components/fhir-card/datatypes/dicom/dicom.component.ts diff --git a/frontend/src/app/components/fhir/datatypes/dicom/dicom.stories.ts b/frontend/src/app/components/fhir-card/datatypes/dicom/dicom.stories.ts similarity index 97% rename from frontend/src/app/components/fhir/datatypes/dicom/dicom.stories.ts rename to frontend/src/app/components/fhir-card/datatypes/dicom/dicom.stories.ts index 0a4a4a91..1f10aa2e 100644 --- a/frontend/src/app/components/fhir/datatypes/dicom/dicom.stories.ts +++ b/frontend/src/app/components/fhir-card/datatypes/dicom/dicom.stories.ts @@ -6,7 +6,7 @@ import {DicomComponent} from "./dicom.component"; // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Datatypes/Dicom', + title: 'Fhir Card/Datatypes/Dicom', component: DicomComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/datatypes/html/html.component.html b/frontend/src/app/components/fhir-card/datatypes/html/html.component.html similarity index 100% rename from frontend/src/app/components/fhir/datatypes/html/html.component.html rename to frontend/src/app/components/fhir-card/datatypes/html/html.component.html diff --git a/frontend/src/app/components/fhir/datatypes/html/html.component.scss b/frontend/src/app/components/fhir-card/datatypes/html/html.component.scss similarity index 100% rename from frontend/src/app/components/fhir/datatypes/html/html.component.scss rename to frontend/src/app/components/fhir-card/datatypes/html/html.component.scss diff --git a/frontend/src/app/components/fhir/datatypes/html/html.component.spec.ts b/frontend/src/app/components/fhir-card/datatypes/html/html.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/html/html.component.spec.ts rename to frontend/src/app/components/fhir-card/datatypes/html/html.component.spec.ts diff --git a/frontend/src/app/components/fhir/datatypes/html/html.component.ts b/frontend/src/app/components/fhir-card/datatypes/html/html.component.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/html/html.component.ts rename to frontend/src/app/components/fhir-card/datatypes/html/html.component.ts diff --git a/frontend/src/app/components/fhir/datatypes/html/html.stories.ts b/frontend/src/app/components/fhir-card/datatypes/html/html.stories.ts similarity index 97% rename from frontend/src/app/components/fhir/datatypes/html/html.stories.ts rename to frontend/src/app/components/fhir-card/datatypes/html/html.stories.ts index 09d0e354..07fdc5db 100644 --- a/frontend/src/app/components/fhir/datatypes/html/html.stories.ts +++ b/frontend/src/app/components/fhir-card/datatypes/html/html.stories.ts @@ -8,7 +8,7 @@ import {HtmlComponent} from "./html.component"; // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Datatypes/Html', + title: 'Fhir Card/Datatypes/Html', component: HtmlComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/datatypes/img/img.component.html b/frontend/src/app/components/fhir-card/datatypes/img/img.component.html similarity index 100% rename from frontend/src/app/components/fhir/datatypes/img/img.component.html rename to frontend/src/app/components/fhir-card/datatypes/img/img.component.html diff --git a/frontend/src/app/components/fhir/datatypes/img/img.component.scss b/frontend/src/app/components/fhir-card/datatypes/img/img.component.scss similarity index 100% rename from frontend/src/app/components/fhir/datatypes/img/img.component.scss rename to frontend/src/app/components/fhir-card/datatypes/img/img.component.scss diff --git a/frontend/src/app/components/fhir/datatypes/img/img.component.spec.ts b/frontend/src/app/components/fhir-card/datatypes/img/img.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/img/img.component.spec.ts rename to frontend/src/app/components/fhir-card/datatypes/img/img.component.spec.ts diff --git a/frontend/src/app/components/fhir/datatypes/img/img.component.ts b/frontend/src/app/components/fhir-card/datatypes/img/img.component.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/img/img.component.ts rename to frontend/src/app/components/fhir-card/datatypes/img/img.component.ts diff --git a/frontend/src/app/components/fhir/datatypes/img/img.stories.ts b/frontend/src/app/components/fhir-card/datatypes/img/img.stories.ts similarity index 97% rename from frontend/src/app/components/fhir/datatypes/img/img.stories.ts rename to frontend/src/app/components/fhir-card/datatypes/img/img.stories.ts index ead9fbf5..d07182c6 100644 --- a/frontend/src/app/components/fhir/datatypes/img/img.stories.ts +++ b/frontend/src/app/components/fhir-card/datatypes/img/img.stories.ts @@ -6,7 +6,7 @@ import {ImgComponent} from "./img.component"; // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Datatypes/Img', + title: 'Fhir Card/Datatypes/Img', component: ImgComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/datatypes/markdown/markdown.component.html b/frontend/src/app/components/fhir-card/datatypes/markdown/markdown.component.html similarity index 100% rename from frontend/src/app/components/fhir/datatypes/markdown/markdown.component.html rename to frontend/src/app/components/fhir-card/datatypes/markdown/markdown.component.html diff --git a/frontend/src/app/components/fhir/datatypes/markdown/markdown.component.scss b/frontend/src/app/components/fhir-card/datatypes/markdown/markdown.component.scss similarity index 100% rename from frontend/src/app/components/fhir/datatypes/markdown/markdown.component.scss rename to frontend/src/app/components/fhir-card/datatypes/markdown/markdown.component.scss diff --git a/frontend/src/app/components/fhir/datatypes/markdown/markdown.component.spec.ts b/frontend/src/app/components/fhir-card/datatypes/markdown/markdown.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/markdown/markdown.component.spec.ts rename to frontend/src/app/components/fhir-card/datatypes/markdown/markdown.component.spec.ts diff --git a/frontend/src/app/components/fhir/datatypes/markdown/markdown.component.ts b/frontend/src/app/components/fhir-card/datatypes/markdown/markdown.component.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/markdown/markdown.component.ts rename to frontend/src/app/components/fhir-card/datatypes/markdown/markdown.component.ts diff --git a/frontend/src/app/components/fhir-card/datatypes/markdown/markdown.stories.ts b/frontend/src/app/components/fhir-card/datatypes/markdown/markdown.stories.ts new file mode 100644 index 00000000..0dd94969 --- /dev/null +++ b/frontend/src/app/components/fhir-card/datatypes/markdown/markdown.stories.ts @@ -0,0 +1,45 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {fhirVersions} from "../../../../../lib/models/constants"; +import R4Example1Json from "../../../../../lib/fixtures/r4/resources/binary/exampleMarkdown.json"; +import {BinaryModel} from "../../../../../lib/models/resources/binary-model"; +import {MarkdownComponent} from "./markdown.component"; + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Fhir Card/Datatypes/Markdown', + component: MarkdownComponent, + decorators: [ + // moduleMetadata({ + // imports: [AppModule] + // }) + // applicationConfig({ + // providers: [importProvidersFrom(AppModule)], + // }), + ], + tags: ['autodocs'], + render: (args: MarkdownComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + displayModel: { + control: 'object', + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +let aiDisplayModel1 = new BinaryModel(R4Example1Json, fhirVersions.R4) +aiDisplayModel1.source_id = '123-456-789' +aiDisplayModel1.source_resource_id = '123-456-789' +export const R4Example1: Story = { + args: { + displayModel: aiDisplayModel1 + } +}; + diff --git a/frontend/src/app/components/fhir/datatypes/pdf/pdf.component.html b/frontend/src/app/components/fhir-card/datatypes/pdf/pdf.component.html similarity index 100% rename from frontend/src/app/components/fhir/datatypes/pdf/pdf.component.html rename to frontend/src/app/components/fhir-card/datatypes/pdf/pdf.component.html diff --git a/frontend/src/app/components/fhir/datatypes/pdf/pdf.component.scss b/frontend/src/app/components/fhir-card/datatypes/pdf/pdf.component.scss similarity index 100% rename from frontend/src/app/components/fhir/datatypes/pdf/pdf.component.scss rename to frontend/src/app/components/fhir-card/datatypes/pdf/pdf.component.scss diff --git a/frontend/src/app/components/fhir/datatypes/pdf/pdf.component.spec.ts b/frontend/src/app/components/fhir-card/datatypes/pdf/pdf.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/pdf/pdf.component.spec.ts rename to frontend/src/app/components/fhir-card/datatypes/pdf/pdf.component.spec.ts diff --git a/frontend/src/app/components/fhir/datatypes/pdf/pdf.component.ts b/frontend/src/app/components/fhir-card/datatypes/pdf/pdf.component.ts similarity index 100% rename from frontend/src/app/components/fhir/datatypes/pdf/pdf.component.ts rename to frontend/src/app/components/fhir-card/datatypes/pdf/pdf.component.ts diff --git a/frontend/src/app/components/fhir/datatypes/pdf/pdf.stories.ts b/frontend/src/app/components/fhir-card/datatypes/pdf/pdf.stories.ts similarity index 97% rename from frontend/src/app/components/fhir/datatypes/pdf/pdf.stories.ts rename to frontend/src/app/components/fhir-card/datatypes/pdf/pdf.stories.ts index 3d46f357..5a129cbe 100644 --- a/frontend/src/app/components/fhir/datatypes/pdf/pdf.stories.ts +++ b/frontend/src/app/components/fhir-card/datatypes/pdf/pdf.stories.ts @@ -6,7 +6,7 @@ import {PdfComponent} from "./pdf.component"; // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Datatypes/Pdf', + title: 'Fhir Card/Datatypes/Pdf', component: PdfComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir-card/fhir-card.module.ts b/frontend/src/app/components/fhir-card/fhir-card.module.ts new file mode 100644 index 00000000..add809fe --- /dev/null +++ b/frontend/src/app/components/fhir-card/fhir-card.module.ts @@ -0,0 +1,106 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import {BadgeComponent} from './common/badge/badge.component'; +import {TableComponent} from './common/table/table.component'; +import {BinaryTextComponent} from './datatypes/binary-text/binary-text.component'; +import {CodableConceptComponent} from './datatypes/codable-concept/codable-concept.component'; +import {CodingComponent} from './datatypes/coding/coding.component'; +import {DicomComponent} from './datatypes/dicom/dicom.component'; +import {HtmlComponent} from './datatypes/html/html.component'; +import {ImgComponent} from './datatypes/img/img.component'; +import {MarkdownComponent} from './datatypes/markdown/markdown.component'; +import {PdfComponent} from './datatypes/pdf/pdf.component'; +import {AllergyIntoleranceComponent} from './resources/allergy-intolerance/allergy-intolerance.component'; +import {BinaryComponent} from './resources/binary/binary.component'; +import {DiagnosticReportComponent} from './resources/diagnostic-report/diagnostic-report.component'; +import {DocumentReferenceComponent} from './resources/document-reference/document-reference.component'; +import {FallbackComponent} from './resources/fallback/fallback.component'; +import {ImmunizationComponent} from './resources/immunization/immunization.component'; +import {LocationComponent} from './resources/location/location.component'; +import {MediaComponent} from './resources/media/media.component'; +import {MedicationComponent} from './resources/medication/medication.component'; +import {MedicationRequestComponent} from './resources/medication-request/medication-request.component'; +import {ObservationComponent} from './resources/observation/observation.component'; +import {OrganizationComponent} from './resources/organization/organization.component'; +import {PractitionerComponent} from './resources/practitioner/practitioner.component'; +import {ProcedureComponent} from './resources/procedure/procedure.component'; +import {FhirCardComponent} from './fhir-card/fhir-card.component'; +import {FhirCardOutletDirective} from './fhir-card/fhir-card-outlet.directive'; +import { EncounterComponent } from './resources/encounter/encounter.component'; + + + +@NgModule({ + imports: [ + //common + CommonModule, + BadgeComponent, + //datatypes + TableComponent, + BinaryTextComponent, + CodableConceptComponent, + CodingComponent, + DicomComponent, + HtmlComponent, + ImgComponent, + MarkdownComponent, + PdfComponent, + //resources + AllergyIntoleranceComponent, + BinaryComponent, + DiagnosticReportComponent, + DocumentReferenceComponent, + EncounterComponent, + FallbackComponent, + ImmunizationComponent, + LocationComponent, + MediaComponent, + MedicationComponent, + MedicationRequestComponent, + ObservationComponent, + OrganizationComponent, + PractitionerComponent, + ProcedureComponent, + + ], + //TODO: every component in here should be migrated to a standalone component + declarations: [ + FhirCardComponent, + FhirCardOutletDirective, + + ], + exports:[ + //common + BadgeComponent, + TableComponent, + //datatypes + BinaryTextComponent, + CodableConceptComponent, + CodingComponent, + DicomComponent, + HtmlComponent, + ImgComponent, + MarkdownComponent, + PdfComponent, + //resources + AllergyIntoleranceComponent, + BinaryComponent, + DiagnosticReportComponent, + DocumentReferenceComponent, + EncounterComponent, + FallbackComponent, + ImmunizationComponent, + LocationComponent, + MediaComponent, + MedicationComponent, + MedicationRequestComponent, + ObservationComponent, + OrganizationComponent, + PractitionerComponent, + ProcedureComponent, + + FhirCardComponent, + FhirCardOutletDirective, + ] +}) +export class FhirCardModule { } diff --git a/frontend/src/app/components/fhir/fhir-resource/fhir-resource-component-interface.ts b/frontend/src/app/components/fhir-card/fhir-card/fhir-card-component-interface.ts similarity index 84% rename from frontend/src/app/components/fhir/fhir-resource/fhir-resource-component-interface.ts rename to frontend/src/app/components/fhir-card/fhir-card/fhir-card-component-interface.ts index 320a1e1b..dd70e977 100644 --- a/frontend/src/app/components/fhir/fhir-resource/fhir-resource-component-interface.ts +++ b/frontend/src/app/components/fhir-card/fhir-card/fhir-card-component-interface.ts @@ -1,9 +1,10 @@ import {FastenDisplayModel} from '../../../../lib/models/fasten/fasten-display-model'; //all Fhir Resource components must implement this Interface -export interface FhirResourceComponentInterface { +export interface FhirCardComponentInterface { displayModel: FastenDisplayModel; showDetails: boolean; + isCollapsed: boolean; //these are used to populate the description of the resource. May not be available for all resources resourceCode?: string; diff --git a/frontend/src/app/components/fhir/fhir-resource/fhir-resource-outlet.directive.spec.ts b/frontend/src/app/components/fhir-card/fhir-card/fhir-card-outlet.directive.spec.ts similarity index 94% rename from frontend/src/app/components/fhir/fhir-resource/fhir-resource-outlet.directive.spec.ts rename to frontend/src/app/components/fhir-card/fhir-card/fhir-card-outlet.directive.spec.ts index 512ab875..cb392d1d 100644 --- a/frontend/src/app/components/fhir/fhir-resource/fhir-resource-outlet.directive.spec.ts +++ b/frontend/src/app/components/fhir-card/fhir-card/fhir-card-outlet.directive.spec.ts @@ -1,4 +1,4 @@ -import { FhirResourceOutletDirective } from './fhir-resource-outlet.directive'; +import { FhirCardOutletDirective } from './fhir-card-outlet.directive'; import { ComponentFactory, ComponentRef, @@ -66,11 +66,11 @@ class TestViewContainerRef extends ViewContainerRef { } -describe('FhirResourceOutletDirective', () => { +describe('FhirCardOutletDirective', () => { it('should create an instance', () => { - const directive = new FhirResourceOutletDirective(new TestViewContainerRef()); + const directive = new FhirCardOutletDirective(new TestViewContainerRef()); expect(directive).toBeTruthy(); }); }); diff --git a/frontend/src/app/components/resource-list/resource-list-outlet.directive.ts b/frontend/src/app/components/fhir-card/fhir-card/fhir-card-outlet.directive.ts similarity index 64% rename from frontend/src/app/components/resource-list/resource-list-outlet.directive.ts rename to frontend/src/app/components/fhir-card/fhir-card/fhir-card-outlet.directive.ts index c8888430..72a1964a 100644 --- a/frontend/src/app/components/resource-list/resource-list-outlet.directive.ts +++ b/frontend/src/app/components/fhir-card/fhir-card/fhir-card-outlet.directive.ts @@ -1,9 +1,9 @@ import {Directive, ViewContainerRef} from '@angular/core'; @Directive({ - selector: '[resourceListOutlet]' + selector: '[fhirCardOutlet]' }) -export class ResourceListOutletDirective { +export class FhirCardOutletDirective { constructor(public viewContainerRef: ViewContainerRef) { } diff --git a/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.html b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.html new file mode 100644 index 00000000..4ba99c89 --- /dev/null +++ b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.html @@ -0,0 +1 @@ + diff --git a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.scss b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.scss similarity index 100% rename from frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.scss rename to frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.scss diff --git a/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.spec.ts b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.spec.ts new file mode 100644 index 00000000..1213c428 --- /dev/null +++ b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FhirCardComponent } from './fhir-card.component'; +import {FhirCardOutletDirective} from './fhir-card-outlet.directive'; + +describe('FhirResourceComponent', () => { + let component: FhirCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FhirCardComponent, FhirCardOutletDirective ], + }) + .compileComponents(); + + fixture = TestBed.createComponent(FhirCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.ts b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.ts similarity index 85% rename from frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.ts rename to frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.ts index f87df80a..02ab8bb3 100644 --- a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.ts +++ b/frontend/src/app/components/fhir-card/fhir-card/fhir-card.component.ts @@ -9,12 +9,12 @@ import { ViewChild } from '@angular/core'; import {BinaryModel} from '../../../../lib/models/resources/binary-model'; -import {FhirResourceOutletDirective} from './fhir-resource-outlet.directive'; +import {FhirCardOutletDirective} from './fhir-card-outlet.directive'; import {ResourceType} from '../../../../lib/models/constants'; import {FallbackComponent} from '../resources/fallback/fallback.component'; import {BinaryComponent} from '../resources/binary/binary.component'; -import {FhirResourceComponentInterface} from './fhir-resource-component-interface'; +import {FhirCardComponentInterface} from './fhir-card-component-interface'; import {ImmunizationComponent} from '../resources/immunization/immunization.component'; import {AllergyIntoleranceComponent} from '../resources/allergy-intolerance/allergy-intolerance.component'; import {MedicationComponent} from '../resources/medication/medication.component'; @@ -28,21 +28,23 @@ import {MediaComponent} from '../resources/media/media.component'; import {LocationComponent} from '../resources/location/location.component'; import {OrganizationComponent} from '../resources/organization/organization.component'; import {ObservationComponent} from '../resources/observation/observation.component'; +import {EncounterComponent} from '../resources/encounter/encounter.component'; @Component({ - selector: 'fhir-resource', + selector: 'fhir-card', changeDetection: ChangeDetectionStrategy.Default, - templateUrl: './fhir-resource.component.html', - styleUrls: ['./fhir-resource.component.scss'] + templateUrl: './fhir-card.component.html', + styleUrls: ['./fhir-card.component.scss'] }) -export class FhirResourceComponent implements OnInit, OnChanges { +export class FhirCardComponent implements OnInit, OnChanges { @Input() displayModel: FastenDisplayModel @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false //location to dynamically load the displayModel - @ViewChild(FhirResourceOutletDirective, {static: true}) fhirResourceOutlet!: FhirResourceOutletDirective; + @ViewChild(FhirCardOutletDirective, {static: true}) fhirCardOutlet!: FhirCardOutletDirective; constructor() { } @@ -55,21 +57,22 @@ export class FhirResourceComponent implements OnInit, OnChanges { loadComponent() { //clear the current outlet - const viewContainerRef = this.fhirResourceOutlet.viewContainerRef; + const viewContainerRef = this.fhirCardOutlet.viewContainerRef; viewContainerRef.clear(); let componentType = this.typeLookup(this.displayModel?.source_resource_type) if(componentType != null){ console.log("Attempting to create fhir display component", this.displayModel, componentType) - const componentRef = viewContainerRef.createComponent(componentType); + const componentRef = viewContainerRef.createComponent(componentType); componentRef.instance.displayModel = this.displayModel; componentRef.instance.showDetails = this.showDetails; + componentRef.instance.isCollapsed = this.isCollapsed; componentRef.instance.markForCheck() } } - typeLookup(resourceType: ResourceType): Type { + typeLookup(resourceType: ResourceType): Type { if(!resourceType){ //dont try to render anything if the resourceType isnt set. return null @@ -114,9 +117,9 @@ export class FhirResourceComponent implements OnInit, OnChanges { case "DocumentReference": { return DocumentReferenceComponent; } - // case "Encounter": { - // return ListEncounterComponent; - // } + case "Encounter": { + return EncounterComponent; + } // case "Goal": { // return ListGoalComponent; // } diff --git a/frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.html b/frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.html rename to frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.html diff --git a/frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.scss b/frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.scss rename to frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.scss diff --git a/frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.spec.ts b/frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.spec.ts diff --git a/frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.ts b/frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.ts similarity index 94% rename from frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.ts rename to frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.ts index aaef1d78..8e5ddc59 100644 --- a/frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.component.ts +++ b/frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; import {Router, RouterModule} from '@angular/router'; import {AllergyIntoleranceModel} from '../../../../../lib/models/resources/allergy-intolerance-model'; @@ -15,11 +15,10 @@ import {TableComponent} from "../../common/table/table.component"; templateUrl: './allergy-intolerance.component.html', styleUrls: ['./allergy-intolerance.component.scss'] }) -export class AllergyIntoleranceComponent implements OnInit, FhirResourceComponentInterface { +export class AllergyIntoleranceComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: AllergyIntoleranceModel @Input() showDetails: boolean = true - - isCollapsed: boolean = false + @Input() isCollapsed: boolean = false tableData: TableRowItem[] = [] diff --git a/frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.stories.ts b/frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.stories.ts similarity index 89% rename from frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.stories.ts rename to frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.stories.ts index 6477bb29..7894bda2 100644 --- a/frontend/src/app/components/fhir/resources/allergy-intolerance/allergy-intolerance.stories.ts +++ b/frontend/src/app/components/fhir-card/resources/allergy-intolerance/allergy-intolerance.stories.ts @@ -10,7 +10,7 @@ import R4Example3Json from "../../../../../lib/fixtures/r4/resources/allergyInto // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/AllergyIntolerance', + title: 'Fhir Card/AllergyIntolerance', component: AllergyIntoleranceComponent, decorators: [ // moduleMetadata({ @@ -51,8 +51,8 @@ export const R4Example1: Story = { }; let aiDisplayModel2 = new AllergyIntoleranceModel(R4Example2Json, fhirVersions.R4) -aiDisplayModel1.source_id = '123-456-789' -aiDisplayModel1.source_resource_id = '123-456-789' +aiDisplayModel2.source_id = '123-456-789' +aiDisplayModel2.source_resource_id = '123-456-789' export const R4Example2: Story = { args: { displayModel: aiDisplayModel2 @@ -60,8 +60,8 @@ export const R4Example2: Story = { }; let aiDisplayModel3 = new AllergyIntoleranceModel(R4Example3Json, fhirVersions.R4) -aiDisplayModel1.source_id = '123-456-789' -aiDisplayModel1.source_resource_id = '123-456-789' +aiDisplayModel3.source_id = '123-456-789' +aiDisplayModel3.source_resource_id = '123-456-789' export const R4Example3: Story = { args: { displayModel: aiDisplayModel3 diff --git a/frontend/src/app/components/fhir/resources/binary/binary.component.html b/frontend/src/app/components/fhir-card/resources/binary/binary.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/binary/binary.component.html rename to frontend/src/app/components/fhir-card/resources/binary/binary.component.html diff --git a/frontend/src/app/components/fhir/resources/binary/binary.component.scss b/frontend/src/app/components/fhir-card/resources/binary/binary.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/binary/binary.component.scss rename to frontend/src/app/components/fhir-card/resources/binary/binary.component.scss diff --git a/frontend/src/app/components/fhir/resources/binary/binary.component.spec.ts b/frontend/src/app/components/fhir-card/resources/binary/binary.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/resources/binary/binary.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/binary/binary.component.spec.ts diff --git a/frontend/src/app/components/fhir/resources/binary/binary.component.ts b/frontend/src/app/components/fhir-card/resources/binary/binary.component.ts similarity index 92% rename from frontend/src/app/components/fhir/resources/binary/binary.component.ts rename to frontend/src/app/components/fhir-card/resources/binary/binary.component.ts index 259deec3..28753ab4 100644 --- a/frontend/src/app/components/fhir/resources/binary/binary.component.ts +++ b/frontend/src/app/components/fhir-card/resources/binary/binary.component.ts @@ -1,6 +1,6 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; import {BinaryModel} from '../../../../../lib/models/resources/binary-model'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {Router, RouterModule} from '@angular/router'; import {AttachmentModel} from '../../../../../lib/models/datatypes/attachment-model'; import {FastenApiService} from '../../../../services/fasten-api.service'; @@ -37,11 +37,12 @@ import {AuthService} from "../../../../services/auth.service"; templateUrl: './binary.component.html', styleUrls: ['./binary.component.scss'] }) -export class BinaryComponent implements OnInit, FhirResourceComponentInterface { +export class BinaryComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: BinaryModel @Input() showDetails: boolean = true @Input() attachmentSourceId: string @Input() attachmentModel: AttachmentModel //can only have attachmentModel or binaryModel, not both. + @Input() isCollapsed: boolean = false loading: boolean = false constructor(public changeRef: ChangeDetectorRef, public router: Router, public fastenApi: FastenApiService) {} diff --git a/frontend/src/app/components/fhir/resources/binary/binary.stories.ts b/frontend/src/app/components/fhir-card/resources/binary/binary.stories.ts similarity index 99% rename from frontend/src/app/components/fhir/resources/binary/binary.stories.ts rename to frontend/src/app/components/fhir-card/resources/binary/binary.stories.ts index 96769c2c..912f8e20 100644 --- a/frontend/src/app/components/fhir/resources/binary/binary.stories.ts +++ b/frontend/src/app/components/fhir-card/resources/binary/binary.stories.ts @@ -18,7 +18,7 @@ import {HTTP_CLIENT_TOKEN} from '../../../../dependency-injection'; // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Binary', + title: 'Fhir Card/Binary', component: BinaryComponent, decorators: [ moduleMetadata({ diff --git a/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.html b/frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.html rename to frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.html diff --git a/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.scss b/frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.scss rename to frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.scss diff --git a/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.spec.ts b/frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.spec.ts similarity index 82% rename from frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.spec.ts index ca405c4f..288b7d37 100644 --- a/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.spec.ts +++ b/frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.spec.ts @@ -2,6 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DiagnosticReportComponent } from './diagnostic-report.component'; import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {RouterTestingModule} from '@angular/router/testing'; describe('DiagnosticReportComponent', () => { let component: DiagnosticReportComponent; @@ -9,8 +10,7 @@ describe('DiagnosticReportComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ DiagnosticReportComponent ], - imports: [NgbCollapseModule] + imports: [NgbCollapseModule, DiagnosticReportComponent, RouterTestingModule] }) .compileComponents(); diff --git a/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.ts b/frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.ts similarity index 69% rename from frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.ts rename to frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.ts index e0091d93..1fbb454a 100644 --- a/frontend/src/app/components/fhir/resources/diagnostic-report/diagnostic-report.component.ts +++ b/frontend/src/app/components/fhir-card/resources/diagnostic-report/diagnostic-report.component.ts @@ -1,22 +1,31 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; -import {Router} from '@angular/router'; +import {Router, RouterModule} from '@angular/router'; import {DiagnosticReportModel} from '../../../../../lib/models/resources/diagnostic-report-model'; +import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule} from '@angular/common'; +import {BadgeComponent} from '../../common/badge/badge.component'; +import {TableComponent} from '../../common/table/table.component'; +import {BinaryComponent} from '../binary/binary.component'; +import {GlossaryLookupComponent} from '../../../glossary-lookup/glossary-lookup.component'; @Component({ - selector: 'app-diagnostic-report', + standalone: true, + imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule, BinaryComponent, GlossaryLookupComponent], + selector: 'fhir-diagnostic-report', templateUrl: './diagnostic-report.component.html', styleUrls: ['./diagnostic-report.component.scss'] }) -export class DiagnosticReportComponent implements OnInit, FhirResourceComponentInterface { +export class DiagnosticReportComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: DiagnosticReportModel @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false + //these are used to populate the description of the resource. May not be available for all resources resourceCode?: string; resourceCodeSystem?: string; - isCollapsed: boolean = false tableData: TableRowItem[] = [] constructor(public changeRef: ChangeDetectorRef, public router: Router) {} diff --git a/frontend/src/app/components/fhir/resources/document-reference/document-reference.component.html b/frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/document-reference/document-reference.component.html rename to frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.html diff --git a/frontend/src/app/components/fhir/resources/document-reference/document-reference.component.scss b/frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/document-reference/document-reference.component.scss rename to frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.scss diff --git a/frontend/src/app/components/fhir/resources/document-reference/document-reference.component.spec.ts b/frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.spec.ts similarity index 80% rename from frontend/src/app/components/fhir/resources/document-reference/document-reference.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.spec.ts index 25e07158..152bc364 100644 --- a/frontend/src/app/components/fhir/resources/document-reference/document-reference.component.spec.ts +++ b/frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.spec.ts @@ -2,6 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DocumentReferenceComponent } from './document-reference.component'; import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {RouterTestingModule} from '@angular/router/testing'; describe('DocumentReferenceComponent', () => { let component: DocumentReferenceComponent; @@ -9,8 +10,8 @@ describe('DocumentReferenceComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [NgbCollapseModule], - declarations: [ DocumentReferenceComponent ] + imports: [NgbCollapseModule, DocumentReferenceComponent, RouterTestingModule], + declarations: [ ] }) .compileComponents(); diff --git a/frontend/src/app/components/fhir/resources/document-reference/document-reference.component.ts b/frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.ts similarity index 64% rename from frontend/src/app/components/fhir/resources/document-reference/document-reference.component.ts rename to frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.ts index 1965a75f..a1d1fc5b 100644 --- a/frontend/src/app/components/fhir/resources/document-reference/document-reference.component.ts +++ b/frontend/src/app/components/fhir-card/resources/document-reference/document-reference.component.ts @@ -1,19 +1,27 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; import {DiagnosticReportModel} from '../../../../../lib/models/resources/diagnostic-report-model'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; -import {Router} from '@angular/router'; +import {Router, RouterModule} from '@angular/router'; import {DocumentReferenceModel} from '../../../../../lib/models/resources/document-reference-model'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; +import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule} from '@angular/common'; +import {BadgeComponent} from '../../common/badge/badge.component'; +import {TableComponent} from '../../common/table/table.component'; +import {BinaryComponent} from '../binary/binary.component'; +import {GlossaryLookupComponent} from '../../../glossary-lookup/glossary-lookup.component'; @Component({ - selector: 'app-document-reference', + standalone: true, + imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule, BinaryComponent], + selector: 'fhir-document-reference', templateUrl: './document-reference.component.html', styleUrls: ['./document-reference.component.scss'] }) -export class DocumentReferenceComponent implements OnInit, FhirResourceComponentInterface { +export class DocumentReferenceComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: DocumentReferenceModel @Input() showDetails: boolean = true - isCollapsed: boolean = false + @Input() isCollapsed: boolean = false tableData: TableRowItem[] = [] constructor(public changeRef: ChangeDetectorRef, public router: Router) {} diff --git a/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.html b/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.html new file mode 100644 index 00000000..ef29c725 --- /dev/null +++ b/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.html @@ -0,0 +1,19 @@ +
+
+
+
{{displayModel?.sort_title || displayModel?.code?.text}}
+

Start date {{displayModel?.period_start | date}}

+
+ + + + + +
+
+ +
+ +
diff --git a/frontend/src/app/components/fhir/resources/fallback/fallback.component.scss b/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/fallback/fallback.component.scss rename to frontend/src/app/components/fhir-card/resources/encounter/encounter.component.scss diff --git a/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.spec.ts b/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.spec.ts new file mode 100644 index 00000000..037f79ec --- /dev/null +++ b/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EncounterComponent } from './encounter.component'; +import {RouterTestingModule} from '@angular/router/testing'; + +describe('EncounterComponent', () => { + let component: EncounterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ EncounterComponent, RouterTestingModule ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(EncounterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.ts b/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.ts new file mode 100644 index 00000000..926bb508 --- /dev/null +++ b/frontend/src/app/components/fhir-card/resources/encounter/encounter.component.ts @@ -0,0 +1,50 @@ +import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; +import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule} from '@angular/common'; +import {BadgeComponent} from '../../common/badge/badge.component'; +import {TableComponent} from '../../common/table/table.component'; +import {GlossaryLookupComponent} from '../../../glossary-lookup/glossary-lookup.component'; +import {Router, RouterModule} from '@angular/router'; +import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; +import {EncounterModel} from '../../../../../lib/models/resources/encounter-model'; + +@Component({ + standalone: true, + imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, GlossaryLookupComponent, RouterModule], + selector: 'fhir-encounter', + templateUrl: './encounter.component.html', + styleUrls: ['./encounter.component.scss'] +}) +export class EncounterComponent implements OnInit, FhirCardComponentInterface { + @Input() displayModel: EncounterModel | null + @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false + + //these are used to populate the description of the resource. May not be available for all resources + resourceCode?: string; + resourceCodeSystem?: string; + + tableData: TableRowItem[] = [] + + constructor(public changeRef: ChangeDetectorRef, public router: Router) { } + + ngOnInit(): void { + this.tableData = [ + { + label: 'Type', + data: this.displayModel?.encounter_type?.[0], + data_type: TableRowItemDataType.CodableConcept, + enabled: !!this.displayModel?.encounter_type?.[0], + }, + { + label: 'Location', + data: this.displayModel?.location_display, + enabled: !!this.displayModel?.location_display, + }, + ]; + } + markForCheck(){ + this.changeRef.markForCheck() + } +} diff --git a/frontend/src/app/components/fhir-card/resources/encounter/encounter.stories.ts b/frontend/src/app/components/fhir-card/resources/encounter/encounter.stories.ts new file mode 100644 index 00000000..801f1a6c --- /dev/null +++ b/frontend/src/app/components/fhir-card/resources/encounter/encounter.stories.ts @@ -0,0 +1,70 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {EncounterComponent} from "./encounter.component"; +import {EncounterModel} from "../../../../../lib/models/resources/encounter-model"; +import {fhirVersions} from "../../../../../lib/models/constants"; +import R4Example1Json from "../../../../../lib/fixtures/r4/resources/encounter/example1.json"; +import R4Example2Json from "../../../../../lib/fixtures/r4/resources/encounter/example2.json"; +import R4Example3Json from "../../../../../lib/fixtures/r4/resources/encounter/example3.json"; + + + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Fhir Card/Encounter', + component: EncounterComponent, + decorators: [ + // moduleMetadata({ + // imports: [AppModule] + // }) + // applicationConfig({ + // providers: [importProvidersFrom(AppModule)], + // }), + ], + tags: ['autodocs'], + render: (args: EncounterComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + displayModel: { + control: 'object', + }, + showDetails: { + control: 'boolean', + } + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +let encounterDisplayModel1 = new EncounterModel(R4Example1Json, fhirVersions.R4) +encounterDisplayModel1.source_id = '123-456-789' +encounterDisplayModel1.source_resource_id = '123-456-789' +export const R4Example1: Story = { + args: { + displayModel: encounterDisplayModel1 + } +}; + +let encounterDisplayModel2 = new EncounterModel(R4Example2Json, fhirVersions.R4) +encounterDisplayModel2.source_id = '123-456-789' +encounterDisplayModel2.source_resource_id = '123-456-789' +export const R4Example2: Story = { + args: { + displayModel: encounterDisplayModel2 + } +}; + +let encounterDisplayModel3 = new EncounterModel(R4Example3Json, fhirVersions.R4) +encounterDisplayModel3.source_id = '123-456-789' +encounterDisplayModel3.source_resource_id = '123-456-789' +export const R4Example3: Story = { + args: { + displayModel: encounterDisplayModel3 + } +}; + diff --git a/frontend/src/app/components/fhir/resources/fallback/fallback.component.html b/frontend/src/app/components/fhir-card/resources/fallback/fallback.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/fallback/fallback.component.html rename to frontend/src/app/components/fhir-card/resources/fallback/fallback.component.html diff --git a/frontend/src/app/components/fhir/resources/immunization/immunization.component.scss b/frontend/src/app/components/fhir-card/resources/fallback/fallback.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/immunization/immunization.component.scss rename to frontend/src/app/components/fhir-card/resources/fallback/fallback.component.scss diff --git a/frontend/src/app/components/fhir/resources/fallback/fallback.component.spec.ts b/frontend/src/app/components/fhir-card/resources/fallback/fallback.component.spec.ts similarity index 93% rename from frontend/src/app/components/fhir/resources/fallback/fallback.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/fallback/fallback.component.spec.ts index 3db533ce..87ee69b2 100644 --- a/frontend/src/app/components/fhir/resources/fallback/fallback.component.spec.ts +++ b/frontend/src/app/components/fhir-card/resources/fallback/fallback.component.spec.ts @@ -8,7 +8,7 @@ describe('FallbackComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ FallbackComponent ] + imports: [ FallbackComponent ] }) .compileComponents(); diff --git a/frontend/src/app/components/fhir/resources/fallback/fallback.component.ts b/frontend/src/app/components/fhir-card/resources/fallback/fallback.component.ts similarity index 55% rename from frontend/src/app/components/fhir/resources/fallback/fallback.component.ts rename to frontend/src/app/components/fhir-card/resources/fallback/fallback.component.ts index 8b41b0a9..c80e2db2 100644 --- a/frontend/src/app/components/fhir/resources/fallback/fallback.component.ts +++ b/frontend/src/app/components/fhir-card/resources/fallback/fallback.component.ts @@ -1,16 +1,22 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; import {BinaryModel} from '../../../../../lib/models/resources/binary-model'; import {Router} from '@angular/router'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; +import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule} from '@angular/common'; +import {HighlightModule} from 'ngx-highlightjs'; @Component({ + standalone: true, + imports: [NgbCollapseModule, HighlightModule, CommonModule], selector: 'fhir-fallback', templateUrl: './fallback.component.html', styleUrls: ['./fallback.component.scss'] }) -export class FallbackComponent implements OnInit, FhirResourceComponentInterface { +export class FallbackComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: BinaryModel @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false constructor(public changeRef: ChangeDetectorRef, public router: Router) {} diff --git a/frontend/src/app/components/fhir/resources/immunization/immunization.component.html b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/immunization/immunization.component.html rename to frontend/src/app/components/fhir-card/resources/immunization/immunization.component.html diff --git a/frontend/src/app/components/fhir/resources/location/location.component.scss b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/location/location.component.scss rename to frontend/src/app/components/fhir-card/resources/immunization/immunization.component.scss diff --git a/frontend/src/app/components/fhir/resources/immunization/immunization.component.spec.ts b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/resources/immunization/immunization.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/immunization/immunization.component.spec.ts diff --git a/frontend/src/app/components/fhir/resources/immunization/immunization.component.ts b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.ts similarity index 92% rename from frontend/src/app/components/fhir/resources/immunization/immunization.component.ts rename to frontend/src/app/components/fhir-card/resources/immunization/immunization.component.ts index 18351c03..916147a1 100644 --- a/frontend/src/app/components/fhir/resources/immunization/immunization.component.ts +++ b/frontend/src/app/components/fhir-card/resources/immunization/immunization.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {Router, RouterModule} from '@angular/router'; import {ImmunizationModel} from '../../../../../lib/models/resources/immunization-model'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; @@ -16,11 +16,10 @@ import {TableComponent} from "../../common/table/table.component"; templateUrl: './immunization.component.html', styleUrls: ['./immunization.component.scss'] }) -export class ImmunizationComponent implements OnInit, FhirResourceComponentInterface { +export class ImmunizationComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: ImmunizationModel @Input() showDetails: boolean = true - - isCollapsed: boolean = false + @Input() isCollapsed: boolean = false tableData: TableRowItem[] = [] diff --git a/frontend/src/app/components/fhir/resources/immunization/immunization.stories.ts b/frontend/src/app/components/fhir-card/resources/immunization/immunization.stories.ts similarity index 98% rename from frontend/src/app/components/fhir/resources/immunization/immunization.stories.ts rename to frontend/src/app/components/fhir-card/resources/immunization/immunization.stories.ts index 0e454c35..de505a4c 100644 --- a/frontend/src/app/components/fhir/resources/immunization/immunization.stories.ts +++ b/frontend/src/app/components/fhir-card/resources/immunization/immunization.stories.ts @@ -10,7 +10,7 @@ import {ImmunizationModel} from "../../../../../lib/models/resources/immunizatio // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Immunization', + title: 'Fhir Card/Immunization', component: ImmunizationComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/resources/location/location.component.html b/frontend/src/app/components/fhir-card/resources/location/location.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/location/location.component.html rename to frontend/src/app/components/fhir-card/resources/location/location.component.html diff --git a/frontend/src/app/components/fhir/resources/media/media.component.scss b/frontend/src/app/components/fhir-card/resources/location/location.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/media/media.component.scss rename to frontend/src/app/components/fhir-card/resources/location/location.component.scss diff --git a/frontend/src/app/components/fhir/resources/location/location.component.spec.ts b/frontend/src/app/components/fhir-card/resources/location/location.component.spec.ts similarity index 82% rename from frontend/src/app/components/fhir/resources/location/location.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/location/location.component.spec.ts index ef7bda14..5606538c 100644 --- a/frontend/src/app/components/fhir/resources/location/location.component.spec.ts +++ b/frontend/src/app/components/fhir-card/resources/location/location.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { LocationComponent } from './location.component'; +import {RouterTestingModule} from '@angular/router/testing'; describe('LocationComponent', () => { let component: LocationComponent; @@ -8,7 +9,7 @@ describe('LocationComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ LocationComponent ] + imports: [ RouterTestingModule, LocationComponent ] }) .compileComponents(); diff --git a/frontend/src/app/components/fhir/resources/location/location.component.ts b/frontend/src/app/components/fhir-card/resources/location/location.component.ts similarity index 92% rename from frontend/src/app/components/fhir/resources/location/location.component.ts rename to frontend/src/app/components/fhir-card/resources/location/location.component.ts index 6d484911..9e30f5df 100644 --- a/frontend/src/app/components/fhir/resources/location/location.component.ts +++ b/frontend/src/app/components/fhir-card/resources/location/location.component.ts @@ -4,7 +4,7 @@ import {CommonModule} from '@angular/common'; import {BadgeComponent} from '../../common/badge/badge.component'; import {TableComponent} from '../../common/table/table.component'; import {Router, RouterModule} from '@angular/router'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {ImmunizationModel} from '../../../../../lib/models/resources/immunization-model'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; import * as _ from 'lodash'; @@ -17,11 +17,11 @@ import {LocationModel} from '../../../../../lib/models/resources/location-model' templateUrl: './location.component.html', styleUrls: ['./location.component.scss'] }) -export class LocationComponent implements OnInit, FhirResourceComponentInterface { +export class LocationComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: LocationModel @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false - isCollapsed: boolean = false tableData: TableRowItem[] = [] constructor(public changeRef: ChangeDetectorRef, public router: Router) { } diff --git a/frontend/src/app/components/fhir-card/resources/location/location.stories.ts b/frontend/src/app/components/fhir-card/resources/location/location.stories.ts new file mode 100644 index 00000000..d1bdfbfa --- /dev/null +++ b/frontend/src/app/components/fhir-card/resources/location/location.stories.ts @@ -0,0 +1,59 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {fhirVersions} from "../../../../../lib/models/constants"; +import R4Example1Json from "../../../../../lib/fixtures/r4/resources/location/example1.json"; +import R4Example2Json from "../../../../../lib/fixtures/r4/resources/location/example2.json"; +import {LocationComponent} from "./location.component"; +import {LocationModel} from "../../../../../lib/models/resources/location-model"; + + + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Fhir Card/Location', + component: LocationComponent, + decorators: [ + // moduleMetadata({ + // imports: [AppModule] + // }) + // applicationConfig({ + // providers: [importProvidersFrom(AppModule)], + // }), + ], + tags: ['autodocs'], + render: (args: LocationComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + displayModel: { + control: 'object', + }, + showDetails: { + control: 'boolean', + } + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +let r4Example1DisplayModel = new LocationModel(R4Example1Json, fhirVersions.R4) +r4Example1DisplayModel.source_id = '123-456-789' +r4Example1DisplayModel.source_resource_id = '123-456-789' +export const R4Example1: Story = { + args: { + displayModel: r4Example1DisplayModel + } +}; + +let r4Example2DisplayModel = new LocationModel(R4Example2Json, fhirVersions.R4) +r4Example2DisplayModel.source_id = '123-456-789' +r4Example2DisplayModel.source_resource_id = '123-456-789' +export const R4Example2: Story = { + args: { + displayModel: r4Example2DisplayModel + } +}; diff --git a/frontend/src/app/components/fhir/resources/media/media.component.html b/frontend/src/app/components/fhir-card/resources/media/media.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/media/media.component.html rename to frontend/src/app/components/fhir-card/resources/media/media.component.html diff --git a/frontend/src/app/components/fhir/resources/medication-request/medication-request.component.scss b/frontend/src/app/components/fhir-card/resources/media/media.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/medication-request/medication-request.component.scss rename to frontend/src/app/components/fhir-card/resources/media/media.component.scss diff --git a/frontend/src/app/components/fhir/resources/media/media.component.spec.ts b/frontend/src/app/components/fhir-card/resources/media/media.component.spec.ts similarity index 87% rename from frontend/src/app/components/fhir/resources/media/media.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/media/media.component.spec.ts index e1c57eb2..a49c0f71 100644 --- a/frontend/src/app/components/fhir/resources/media/media.component.spec.ts +++ b/frontend/src/app/components/fhir-card/resources/media/media.component.spec.ts @@ -9,9 +9,7 @@ describe('MediaComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ MediaComponent ], - imports: [NgbCollapseModule, RouterTestingModule], - + imports: [NgbCollapseModule, RouterTestingModule, MediaComponent], }) .compileComponents(); diff --git a/frontend/src/app/components/fhir/resources/media/media.component.ts b/frontend/src/app/components/fhir-card/resources/media/media.component.ts similarity index 63% rename from frontend/src/app/components/fhir/resources/media/media.component.ts rename to frontend/src/app/components/fhir-card/resources/media/media.component.ts index aa0a5acf..64ad2062 100644 --- a/frontend/src/app/components/fhir/resources/media/media.component.ts +++ b/frontend/src/app/components/fhir-card/resources/media/media.component.ts @@ -1,19 +1,26 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; import {DocumentReferenceModel} from '../../../../../lib/models/resources/document-reference-model'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; -import {Router} from '@angular/router'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {Router, RouterModule} from '@angular/router'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {MediaModel} from '../../../../../lib/models/resources/media-model'; +import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule} from '@angular/common'; +import {BadgeComponent} from '../../common/badge/badge.component'; +import {TableComponent} from '../../common/table/table.component'; +import {BinaryComponent} from '../binary/binary.component'; @Component({ - selector: 'app-media', + standalone: true, + imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule, BinaryComponent], + selector: 'fhir-media', templateUrl: './media.component.html', styleUrls: ['./media.component.scss'] }) -export class MediaComponent implements OnInit, FhirResourceComponentInterface{ +export class MediaComponent implements OnInit, FhirCardComponentInterface{ @Input() displayModel: MediaModel @Input() showDetails: boolean = true - isCollapsed: boolean = false + @Input() isCollapsed: boolean = false tableData: TableRowItem[] = [] constructor(public changeRef: ChangeDetectorRef, public router: Router) {} diff --git a/frontend/src/app/components/fhir/resources/medication-request/medication-request.component.html b/frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/medication-request/medication-request.component.html rename to frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.html diff --git a/frontend/src/app/components/fhir/resources/medication/medication.component.scss b/frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/medication/medication.component.scss rename to frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.scss diff --git a/frontend/src/app/components/fhir/resources/medication-request/medication-request.component.spec.ts b/frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/resources/medication-request/medication-request.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.spec.ts diff --git a/frontend/src/app/components/fhir/resources/medication-request/medication-request.component.ts b/frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.ts similarity index 92% rename from frontend/src/app/components/fhir/resources/medication-request/medication-request.component.ts rename to frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.ts index bb7c26ef..766c91ca 100644 --- a/frontend/src/app/components/fhir/resources/medication-request/medication-request.component.ts +++ b/frontend/src/app/components/fhir-card/resources/medication-request/medication-request.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; import {Router, RouterModule} from '@angular/router'; import {MedicationRequestModel} from '../../../../../lib/models/resources/medication-request-model'; @@ -16,15 +16,15 @@ import {GlossaryLookupComponent} from '../../../glossary-lookup/glossary-lookup. templateUrl: './medication-request.component.html', styleUrls: ['./medication-request.component.scss'] }) -export class MedicationRequestComponent implements OnInit, FhirResourceComponentInterface { +export class MedicationRequestComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: MedicationRequestModel | null @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false + //these are used to populate the description of the resource. May not be available for all resources resourceCode?: string; resourceCodeSystem?: string; - isCollapsed: boolean = false - tableData: TableRowItem[] = [] constructor(public changeRef: ChangeDetectorRef, public router: Router) {} diff --git a/frontend/src/app/components/fhir/resources/medication-request/medication-request.stories.ts b/frontend/src/app/components/fhir-card/resources/medication-request/medication-request.stories.ts similarity index 98% rename from frontend/src/app/components/fhir/resources/medication-request/medication-request.stories.ts rename to frontend/src/app/components/fhir-card/resources/medication-request/medication-request.stories.ts index 2d593ed4..d4d0bc3a 100644 --- a/frontend/src/app/components/fhir/resources/medication-request/medication-request.stories.ts +++ b/frontend/src/app/components/fhir-card/resources/medication-request/medication-request.stories.ts @@ -11,7 +11,7 @@ import {MedicationRequestModel} from "../../../../../lib/models/resources/medica // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/MedicationRequest', + title: 'Fhir Card/MedicationRequest', component: MedicationRequestComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/resources/medication/medication.component.html b/frontend/src/app/components/fhir-card/resources/medication/medication.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/medication/medication.component.html rename to frontend/src/app/components/fhir-card/resources/medication/medication.component.html diff --git a/frontend/src/app/components/fhir/resources/observation/observation.component.scss b/frontend/src/app/components/fhir-card/resources/medication/medication.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/observation/observation.component.scss rename to frontend/src/app/components/fhir-card/resources/medication/medication.component.scss diff --git a/frontend/src/app/components/fhir/resources/medication/medication.component.spec.ts b/frontend/src/app/components/fhir-card/resources/medication/medication.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/resources/medication/medication.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/medication/medication.component.spec.ts diff --git a/frontend/src/app/components/fhir/resources/medication/medication.component.ts b/frontend/src/app/components/fhir-card/resources/medication/medication.component.ts similarity index 92% rename from frontend/src/app/components/fhir/resources/medication/medication.component.ts rename to frontend/src/app/components/fhir-card/resources/medication/medication.component.ts index a1f1d8a1..bc57b95c 100644 --- a/frontend/src/app/components/fhir/resources/medication/medication.component.ts +++ b/frontend/src/app/components/fhir-card/resources/medication/medication.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; import {Router, RouterModule} from '@angular/router'; import {MedicationModel} from '../../../../../lib/models/resources/medication-model'; @@ -17,16 +17,15 @@ import * as _ from "lodash"; templateUrl: './medication.component.html', styleUrls: ['./medication.component.scss'] }) -export class MedicationComponent implements OnInit, FhirResourceComponentInterface { +export class MedicationComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: MedicationModel @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false //these are used to populate the description of the resource. May not be available for all resources resourceCode?: string; resourceCodeSystem?: string; - isCollapsed: boolean = false - tableData: TableRowItem[] = [] constructor(public changeRef: ChangeDetectorRef, public router: Router) {} diff --git a/frontend/src/app/components/fhir/resources/medication/medication.stories.ts b/frontend/src/app/components/fhir-card/resources/medication/medication.stories.ts similarity index 98% rename from frontend/src/app/components/fhir/resources/medication/medication.stories.ts rename to frontend/src/app/components/fhir-card/resources/medication/medication.stories.ts index 89e80f1b..de4e0a7b 100644 --- a/frontend/src/app/components/fhir/resources/medication/medication.stories.ts +++ b/frontend/src/app/components/fhir-card/resources/medication/medication.stories.ts @@ -10,7 +10,7 @@ import {MedicationModel} from "../../../../../lib/models/resources/medication-mo // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Medication', + title: 'Fhir Card/Medication', component: MedicationComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/resources/observation/observation.component.html b/frontend/src/app/components/fhir-card/resources/observation/observation.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/observation/observation.component.html rename to frontend/src/app/components/fhir-card/resources/observation/observation.component.html diff --git a/frontend/src/app/components/fhir/resources/organization/organization.component.scss b/frontend/src/app/components/fhir-card/resources/observation/observation.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/organization/organization.component.scss rename to frontend/src/app/components/fhir-card/resources/observation/observation.component.scss diff --git a/frontend/src/app/components/fhir/resources/observation/observation.component.spec.ts b/frontend/src/app/components/fhir-card/resources/observation/observation.component.spec.ts similarity index 82% rename from frontend/src/app/components/fhir/resources/observation/observation.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/observation/observation.component.spec.ts index 9f8399ed..c4adf3ce 100644 --- a/frontend/src/app/components/fhir/resources/observation/observation.component.spec.ts +++ b/frontend/src/app/components/fhir-card/resources/observation/observation.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ObservationComponent } from './observation.component'; +import {RouterTestingModule} from '@angular/router/testing'; describe('ObservationComponent', () => { let component: ObservationComponent; @@ -8,7 +9,7 @@ describe('ObservationComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ObservationComponent ] + imports: [ ObservationComponent, RouterTestingModule ] }) .compileComponents(); diff --git a/frontend/src/app/components/fhir/resources/observation/observation.component.ts b/frontend/src/app/components/fhir-card/resources/observation/observation.component.ts similarity index 88% rename from frontend/src/app/components/fhir/resources/observation/observation.component.ts rename to frontend/src/app/components/fhir-card/resources/observation/observation.component.ts index 174be02c..6ee0c851 100644 --- a/frontend/src/app/components/fhir/resources/observation/observation.component.ts +++ b/frontend/src/app/components/fhir-card/resources/observation/observation.component.ts @@ -21,8 +21,8 @@ import {NgChartsModule} from 'ng2-charts'; export class ObservationComponent implements OnInit { @Input() displayModel: ObservationModel @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false - isCollapsed: boolean = false tableData: TableRowItem[] = [] //observation chart data @@ -103,6 +103,9 @@ export class ObservationComponent implements OnInit { constructor(public changeRef: ChangeDetectorRef, public router: Router) { } ngOnInit(): void { + if(!this.displayModel){ + return + } this.tableData.push( { label: 'Issued on', @@ -136,13 +139,13 @@ export class ObservationComponent implements OnInit { //populate chart data this.barChartLabels.push( - formatDate(this.displayModel.effective_date, "mediumDate", "en-US", undefined) + formatDate(this.displayModel?.effective_date, "mediumDate", "en-US", undefined) ) - this.barChartData[0].data = [[this.displayModel.reference_range?.[0]?.low?.value, this.displayModel.reference_range?.[0]?.high?.value]] - this.barChartData[1].data = [[this.displayModel.value_quantity_value as number, this.displayModel.value_quantity_value as number]] + this.barChartData[0].data = [[this.displayModel?.reference_range?.[0]?.low?.value, this.displayModel?.reference_range?.[0]?.high?.value]] + this.barChartData[1].data = [[this.displayModel?.value_quantity_value as number, this.displayModel?.value_quantity_value as number]] - let suggestedMax = (this.displayModel.value_quantity_value as number) * 1.1; + let suggestedMax = (this.displayModel?.value_quantity_value as number) * 1.1; this.barChartOptions.scales['x']['suggestedMax'] = suggestedMax console.log("Observation chart data: ", this.barChartData[0].data, this.barChartData[1].data) diff --git a/frontend/src/app/components/fhir-card/resources/observation/observation.stories.ts b/frontend/src/app/components/fhir-card/resources/observation/observation.stories.ts new file mode 100644 index 00000000..1693cdb9 --- /dev/null +++ b/frontend/src/app/components/fhir-card/resources/observation/observation.stories.ts @@ -0,0 +1,69 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {fhirVersions} from "../../../../../lib/models/constants"; +import R4Example1Json from "../../../../../lib/fixtures/r4/resources/observation/example1.json"; +import R4Example2Json from "../../../../../lib/fixtures/r4/resources/observation/example2.json"; +import R4Example3Json from "../../../../../lib/fixtures/r4/resources/observation/example3.json"; +import {ObservationComponent} from "./observation.component"; +import {ObservationModel} from "../../../../../lib/models/resources/observation-model"; + + + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Fhir Card/Observation', + component: ObservationComponent, + decorators: [ + // moduleMetadata({ + // imports: [AppModule] + // }) + // applicationConfig({ + // providers: [importProvidersFrom(AppModule)], + // }), + ], + tags: ['autodocs'], + render: (args: ObservationComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + displayModel: { + control: 'object', + }, + showDetails: { + control: 'boolean', + } + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +let r4Example1DisplayModel = new ObservationModel(R4Example1Json, fhirVersions.R4) +r4Example1DisplayModel.source_id = '123-456-789' +r4Example1DisplayModel.source_resource_id = '123-456-789' +export const R4Example1: Story = { + args: { + displayModel: r4Example1DisplayModel + } +}; + +let r4Example2DisplayModel = new ObservationModel(R4Example2Json, fhirVersions.R4) +r4Example2DisplayModel.source_id = '123-456-789' +r4Example2DisplayModel.source_resource_id = '123-456-789' +export const R4Example2: Story = { + args: { + displayModel: r4Example2DisplayModel + } +}; + +let r4Example3DisplayModel = new ObservationModel(R4Example3Json, fhirVersions.R4) +r4Example3DisplayModel.source_id = '123-456-789' +r4Example3DisplayModel.source_resource_id = '123-456-789' +export const R4Example3: Story = { + args: { + displayModel: r4Example3DisplayModel + } +}; diff --git a/frontend/src/app/components/fhir/resources/organization/organization.component.html b/frontend/src/app/components/fhir-card/resources/organization/organization.component.html similarity index 79% rename from frontend/src/app/components/fhir/resources/organization/organization.component.html rename to frontend/src/app/components/fhir-card/resources/organization/organization.component.html index e48b81b3..4790e0b3 100644 --- a/frontend/src/app/components/fhir/resources/organization/organization.component.html +++ b/frontend/src/app/components/fhir-card/resources/organization/organization.component.html @@ -3,11 +3,7 @@
{{displayModel?.name}}
- - - - - +

A formally or informally recognized grouping of people or organizations formed for the purpose of achieving some form of collective action. Includes companies, institutions, corporations, departments, community groups, healthcare practice groups, payer/insurer, etc.

diff --git a/frontend/src/app/components/fhir/resources/practitioner/practitioner.component.scss b/frontend/src/app/components/fhir-card/resources/organization/organization.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/practitioner/practitioner.component.scss rename to frontend/src/app/components/fhir-card/resources/organization/organization.component.scss diff --git a/frontend/src/app/components/fhir/resources/organization/organization.component.spec.ts b/frontend/src/app/components/fhir-card/resources/organization/organization.component.spec.ts similarity index 82% rename from frontend/src/app/components/fhir/resources/organization/organization.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/organization/organization.component.spec.ts index 2f0e5fbe..f68bd421 100644 --- a/frontend/src/app/components/fhir/resources/organization/organization.component.spec.ts +++ b/frontend/src/app/components/fhir-card/resources/organization/organization.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { OrganizationComponent } from './organization.component'; +import {RouterTestingModule} from '@angular/router/testing'; describe('OrganizationComponent', () => { let component: OrganizationComponent; @@ -8,7 +9,7 @@ describe('OrganizationComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ OrganizationComponent ] + imports: [ OrganizationComponent, RouterTestingModule ] }) .compileComponents(); diff --git a/frontend/src/app/components/fhir/resources/organization/organization.component.ts b/frontend/src/app/components/fhir-card/resources/organization/organization.component.ts similarity index 98% rename from frontend/src/app/components/fhir/resources/organization/organization.component.ts rename to frontend/src/app/components/fhir-card/resources/organization/organization.component.ts index 96a70241..b7b88316 100644 --- a/frontend/src/app/components/fhir/resources/organization/organization.component.ts +++ b/frontend/src/app/components/fhir-card/resources/organization/organization.component.ts @@ -18,8 +18,8 @@ import {OrganizationModel} from '../../../../../lib/models/resources/organizatio export class OrganizationComponent implements OnInit { @Input() displayModel: OrganizationModel @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false - isCollapsed: boolean = false tableData: TableRowItem[] = [] constructor(public changeRef: ChangeDetectorRef, public router: Router) { } diff --git a/frontend/src/app/components/fhir-card/resources/organization/organization.stories.ts b/frontend/src/app/components/fhir-card/resources/organization/organization.stories.ts new file mode 100644 index 00000000..66659b5c --- /dev/null +++ b/frontend/src/app/components/fhir-card/resources/organization/organization.stories.ts @@ -0,0 +1,69 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {fhirVersions} from "../../../../../lib/models/constants"; +import R4Example1Json from "../../../../../lib/fixtures/r4/resources/organization/example1.json"; +import R4Example2Json from "../../../../../lib/fixtures/r4/resources/organization/example2.json"; +import R4Example3Json from "../../../../../lib/fixtures/r4/resources/organization/example3.json"; +import {OrganizationComponent} from "./organization.component"; +import {OrganizationModel} from "../../../../../lib/models/resources/organization-model"; + + + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Fhir Card/Organization', + component: OrganizationComponent, + decorators: [ + // moduleMetadata({ + // imports: [AppModule] + // }) + // applicationConfig({ + // providers: [importProvidersFrom(AppModule)], + // }), + ], + tags: ['autodocs'], + render: (args: OrganizationComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + displayModel: { + control: 'object', + }, + showDetails: { + control: 'boolean', + } + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +let r4Example1DisplayModel = new OrganizationModel(R4Example1Json, fhirVersions.R4) +r4Example1DisplayModel.source_id = '123-456-789' +r4Example1DisplayModel.source_resource_id = '123-456-789' +export const R4Example1: Story = { + args: { + displayModel: r4Example1DisplayModel + } +}; + +let r4Example2DisplayModel = new OrganizationModel(R4Example2Json, fhirVersions.R4) +r4Example2DisplayModel.source_id = '123-456-789' +r4Example2DisplayModel.source_resource_id = '123-456-789' +export const R4Example2: Story = { + args: { + displayModel: r4Example2DisplayModel + } +}; + +let r4Example3DisplayModel = new OrganizationModel(R4Example3Json, fhirVersions.R4) +r4Example3DisplayModel.source_id = '123-456-789' +r4Example3DisplayModel.source_resource_id = '123-456-789' +export const R4Example3: Story = { + args: { + displayModel: r4Example3DisplayModel + } +}; diff --git a/frontend/src/app/components/fhir/resources/practitioner/practitioner.component.html b/frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.html similarity index 72% rename from frontend/src/app/components/fhir/resources/practitioner/practitioner.component.html rename to frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.html index 1ff00e02..f5c5f06e 100644 --- a/frontend/src/app/components/fhir/resources/practitioner/practitioner.component.html +++ b/frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.html @@ -4,12 +4,10 @@
{{displayModel?.sort_title || displayModel?.name?.[0]?.displayName}}

Date{{displayModel?.sort_date}}

- {{displayModel?.status}} - - - - - +
+ {{displayModel?.status}} + +

A person who is directly or indirectly involved in the provisioning of healthcare.

diff --git a/frontend/src/app/components/fhir/resources/procedure/procedure.component.scss b/frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.scss similarity index 100% rename from frontend/src/app/components/fhir/resources/procedure/procedure.component.scss rename to frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.scss diff --git a/frontend/src/app/components/fhir/resources/practitioner/practitioner.component.spec.ts b/frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/resources/practitioner/practitioner.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.spec.ts diff --git a/frontend/src/app/components/fhir/resources/practitioner/practitioner.component.ts b/frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.ts similarity index 88% rename from frontend/src/app/components/fhir/resources/practitioner/practitioner.component.ts rename to frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.ts index b5c3037e..a5873ace 100644 --- a/frontend/src/app/components/fhir/resources/practitioner/practitioner.component.ts +++ b/frontend/src/app/components/fhir-card/resources/practitioner/practitioner.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {ImmunizationModel} from '../../../../../lib/models/resources/immunization-model'; import {TableRowItem} from '../../common/table/table-row-item'; import {Router, RouterModule} from '@angular/router'; @@ -12,15 +12,14 @@ import {TableComponent} from "../../common/table/table.component"; @Component({ standalone: true, imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent, RouterModule], - selector: 'app-practitioner', + selector: 'fhir-practitioner', templateUrl: './practitioner.component.html', styleUrls: ['./practitioner.component.scss'] }) -export class PractitionerComponent implements OnInit, FhirResourceComponentInterface { +export class PractitionerComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: PractitionerModel | null @Input() showDetails: boolean = true - - isCollapsed: boolean = false + @Input() isCollapsed: boolean = false tableData: TableRowItem[] = [] @@ -56,8 +55,8 @@ export class PractitionerComponent implements OnInit, FhirResourceComponentInter enabled: true, }) } - if(this.displayModel?.address){ - let address = this.displayModel?.address + if(this.displayModel?.address?.length > 0){ + let address = this.displayModel?.address?.[0] let addressParts = [] if(address.line){ addressParts.push(address.line.join(' ')) diff --git a/frontend/src/app/components/fhir/resources/practitioner/practitioner.stories.ts b/frontend/src/app/components/fhir-card/resources/practitioner/practitioner.stories.ts similarity index 98% rename from frontend/src/app/components/fhir/resources/practitioner/practitioner.stories.ts rename to frontend/src/app/components/fhir-card/resources/practitioner/practitioner.stories.ts index c65812cf..cf7c2c05 100644 --- a/frontend/src/app/components/fhir/resources/practitioner/practitioner.stories.ts +++ b/frontend/src/app/components/fhir-card/resources/practitioner/practitioner.stories.ts @@ -10,7 +10,7 @@ import {PractitionerModel} from "../../../../../lib/models/resources/practitione // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Practitioner', + title: 'Fhir Card/Practitioner', component: PractitionerComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir/resources/procedure/procedure.component.html b/frontend/src/app/components/fhir-card/resources/procedure/procedure.component.html similarity index 100% rename from frontend/src/app/components/fhir/resources/procedure/procedure.component.html rename to frontend/src/app/components/fhir-card/resources/procedure/procedure.component.html diff --git a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.scss b/frontend/src/app/components/fhir-card/resources/procedure/procedure.component.scss similarity index 100% rename from frontend/src/app/components/list-generic-resource/list-generic-resource.component.scss rename to frontend/src/app/components/fhir-card/resources/procedure/procedure.component.scss diff --git a/frontend/src/app/components/fhir/resources/procedure/procedure.component.spec.ts b/frontend/src/app/components/fhir-card/resources/procedure/procedure.component.spec.ts similarity index 100% rename from frontend/src/app/components/fhir/resources/procedure/procedure.component.spec.ts rename to frontend/src/app/components/fhir-card/resources/procedure/procedure.component.spec.ts diff --git a/frontend/src/app/components/fhir/resources/procedure/procedure.component.ts b/frontend/src/app/components/fhir-card/resources/procedure/procedure.component.ts similarity index 93% rename from frontend/src/app/components/fhir/resources/procedure/procedure.component.ts rename to frontend/src/app/components/fhir-card/resources/procedure/procedure.component.ts index e73dcc10..075746a3 100644 --- a/frontend/src/app/components/fhir/resources/procedure/procedure.component.ts +++ b/frontend/src/app/components/fhir-card/resources/procedure/procedure.component.ts @@ -1,5 +1,5 @@ import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core'; -import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-component-interface'; +import {FhirCardComponentInterface} from '../../fhir-card/fhir-card-component-interface'; import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item'; import {Router, RouterModule} from '@angular/router'; import {ProcedureModel} from '../../../../../lib/models/resources/procedure-model'; @@ -16,16 +16,15 @@ import {GlossaryLookupComponent} from '../../../glossary-lookup/glossary-lookup. templateUrl: './procedure.component.html', styleUrls: ['./procedure.component.scss'] }) -export class ProcedureComponent implements OnInit, FhirResourceComponentInterface { +export class ProcedureComponent implements OnInit, FhirCardComponentInterface { @Input() displayModel: ProcedureModel | null @Input() showDetails: boolean = true + @Input() isCollapsed: boolean = false //these are used to populate the description of the resource. May not be available for all resources resourceCode?: string; resourceCodeSystem?: string; - isCollapsed: boolean = false - tableData: TableRowItem[] = [] constructor(public changeRef: ChangeDetectorRef, public router: Router) {} diff --git a/frontend/src/app/components/fhir/resources/procedure/procedure.stories.ts b/frontend/src/app/components/fhir-card/resources/procedure/procedure.stories.ts similarity index 98% rename from frontend/src/app/components/fhir/resources/procedure/procedure.stories.ts rename to frontend/src/app/components/fhir-card/resources/procedure/procedure.stories.ts index 97f3e44b..4ba15539 100644 --- a/frontend/src/app/components/fhir/resources/procedure/procedure.stories.ts +++ b/frontend/src/app/components/fhir-card/resources/procedure/procedure.stories.ts @@ -10,7 +10,7 @@ import {ProcedureModel} from "../../../../../lib/models/resources/procedure-mode // More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction const meta: Meta = { - title: 'Fhir/Procedure', + title: 'Fhir Card/Procedure', component: ProcedureComponent, decorators: [ // moduleMetadata({ diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-adverse-event.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-adverse-event.component.ts new file mode 100644 index 00000000..d196dd15 --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-adverse-event.component.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; +import {attributeXTime} from './utils'; + +@Component({ + selector: 'fhir-datatable-adverse-event', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] +}) +export class DatatableAdverseEventComponent extends DatatableGenericResourceComponent { + columnDefinitions: GenericColumnDefn[] = [ + { title: 'Event', versions: '*', format: 'codeableConcept', getter: a => a.event }, + { title: 'Date', versions: '*', format: 'date', getter: a => a.date } + ] +} diff --git a/frontend/src/app/components/list-generic-resource/list-allergy-intolerance.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-allergy-intolerance.component.ts similarity index 66% rename from frontend/src/app/components/list-generic-resource/list-allergy-intolerance.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-allergy-intolerance.component.ts index 2d57781c..4d599a50 100644 --- a/frontend/src/app/components/list-generic-resource/list-allergy-intolerance.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-allergy-intolerance.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-allergy-intolerance', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-allergy-intolerance', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListAllergyIntoleranceComponent extends ListGenericResourceComponent { +export class DatatableAllergyIntoleranceComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Date Recorded', versions: '*', format: 'date', getter: a => a.assertedDate || a.recordedDate }, { title: 'Allergy Type', versions: '*', getter: a => a.category[0] }, // Allergy Type diff --git a/frontend/src/app/components/list-generic-resource/list-appointment.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-appointment.component.ts similarity index 56% rename from frontend/src/app/components/list-generic-resource/list-appointment.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-appointment.component.ts index e0e372fe..119d2f1b 100644 --- a/frontend/src/app/components/list-generic-resource/list-appointment.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-appointment.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-appointment', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-appointment', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListAppointmentComponent extends ListGenericResourceComponent { +export class DatatableAppointmentComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Type', versions: '*', format: 'codeableConcept', getter: a => a.serviceType }, { title: 'Status', versions: '*', getter: a => a.status }, diff --git a/frontend/src/app/components/list-generic-resource/list-binary.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-binary.component.ts similarity index 55% rename from frontend/src/app/components/list-generic-resource/list-binary.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-binary.component.ts index 4352d1e9..1a7b5a14 100644 --- a/frontend/src/app/components/list-generic-resource/list-binary.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-binary.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-binary', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-binary', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListBinaryComponent extends ListGenericResourceComponent { +export class DatatableBinaryComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Content-Type', versions: '*', getter: c => c.contentType }, { title: 'ID', versions: '*', getter: c => c.id }, diff --git a/frontend/src/app/components/list-generic-resource/list-care-plan.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-care-plan.component.ts similarity index 64% rename from frontend/src/app/components/list-generic-resource/list-care-plan.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-care-plan.component.ts index f8b55109..cd18a01f 100644 --- a/frontend/src/app/components/list-generic-resource/list-care-plan.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-care-plan.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-care-plan', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-care-plan', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListCarePlanComponent extends ListGenericResourceComponent { +export class DatatableCarePlanComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Category', versions: '*', format: 'code', getter: c => c.category[0].coding[0] }, { title: 'Reason', versions: '*', getter: c => { diff --git a/frontend/src/app/components/list-generic-resource/list-care-team.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-care-team.component.ts similarity index 55% rename from frontend/src/app/components/list-generic-resource/list-care-team.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-care-team.component.ts index f9a4aa11..16653b36 100644 --- a/frontend/src/app/components/list-generic-resource/list-care-team.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-care-team.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-care-team', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-care-team', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListCareTeamComponent extends ListGenericResourceComponent { +export class DatatableCareTeamComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Category', versions: '*', format: 'codableConcept', getter: c => c.category[0] }, { title: 'Name', versions: '*', getter: c => c.name }, diff --git a/frontend/src/app/components/list-generic-resource/list-communication.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-communication.component.ts similarity index 56% rename from frontend/src/app/components/list-generic-resource/list-communication.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-communication.component.ts index 214051c0..d070bdd1 100644 --- a/frontend/src/app/components/list-generic-resource/list-communication.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-communication.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-communication', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-communication', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListCommunicationComponent extends ListGenericResourceComponent { +export class DatatableCommunicationComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Reason', versions: '*', format: 'code', getter: c => c.reasonCode[0].coding[0] }, { title: 'Sent', versions: '*', format: 'date', getter: c => c.sent }, diff --git a/frontend/src/app/components/list-generic-resource/list-condition.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-condition.component.ts similarity index 65% rename from frontend/src/app/components/list-generic-resource/list-condition.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-condition.component.ts index 43b2eb0d..c57f6267 100644 --- a/frontend/src/app/components/list-generic-resource/list-condition.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-condition.component.ts @@ -1,13 +1,13 @@ import {Component, OnInit} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent, ResourceListComponentInterface} from './datatable-generic-resource.component'; import {FORMATTERS} from './utils'; @Component({ - selector: 'app-list-condition', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-condition', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListConditionComponent extends ListGenericResourceComponent { +export class DatatableConditionComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Condition', versions: '*', format: 'codeableConcept', getter: c => c.code }, { title: 'Date of Onset', versions: '*', format: 'date', getter: c => c.onsetDateTime }, diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-coverage.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-coverage.component.ts new file mode 100644 index 00000000..d876a211 --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-coverage.component.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; +import {attributeXTime} from './utils'; + +@Component({ + selector: 'fhir-datatable-coverage', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] +}) +export class DatatableCoverageComponent extends DatatableGenericResourceComponent { + columnDefinitions: GenericColumnDefn[] = [ + { title: 'Type', versions: '*', format: 'codeableConcept', getter: c => c.type }, + { title: 'Period', versions: '*', format: 'period', getter: c => c.period } + ] +} diff --git a/frontend/src/app/components/list-generic-resource/list-device-request.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-device-request.component.ts similarity index 60% rename from frontend/src/app/components/list-generic-resource/list-device-request.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-device-request.component.ts index 54a5f4d6..5e7f8bff 100644 --- a/frontend/src/app/components/list-generic-resource/list-device-request.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-device-request.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-device-request', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-device-request', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListDeviceRequestComponent extends ListGenericResourceComponent { +export class DatatableDeviceRequestComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Device', versions: '*', format: 'codeableConcept', getter: d => d.codeCodeableConcept }, { title: 'Author Date', versions: '*', format: 'date', getter: d => d.authoredOn }, diff --git a/frontend/src/app/components/list-generic-resource/list-device.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-device.component.ts similarity index 63% rename from frontend/src/app/components/list-generic-resource/list-device.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-device.component.ts index eefde082..52ecace4 100644 --- a/frontend/src/app/components/list-generic-resource/list-device.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-device.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-device', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-device', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListDeviceComponent extends ListGenericResourceComponent { +export class DatatableDeviceComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Device', versions: '*', getter: d => d.deviceName?.[0]?.name || d.type?.coding?.[0]?.display || d.type?.text }, { title: 'Manufacturer', versions: '*', getter: d => d.manufacturer }, diff --git a/frontend/src/app/components/list-generic-resource/list-diagnostic-report.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-diagnostic-report.component.ts similarity index 57% rename from frontend/src/app/components/list-generic-resource/list-diagnostic-report.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-diagnostic-report.component.ts index 54e2dc1c..0150ffbc 100644 --- a/frontend/src/app/components/list-generic-resource/list-diagnostic-report.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-diagnostic-report.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-diagnostic-report', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-diagnostic-report', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListDiagnosticReportComponent extends ListGenericResourceComponent { +export class DatatableDiagnosticReportComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Issued', versions: '*', format: 'date', getter: d => d.issued }, { title: 'Title', versions: '*', format: 'codeableConcept', getter: d => d.code }, diff --git a/frontend/src/app/components/list-generic-resource/list-document-reference.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-document-reference.component.ts similarity index 61% rename from frontend/src/app/components/list-generic-resource/list-document-reference.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-document-reference.component.ts index 004cf35d..ae4081e0 100644 --- a/frontend/src/app/components/list-generic-resource/list-document-reference.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-document-reference.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-document-reference', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-document-reference', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListDocumentReferenceComponent extends ListGenericResourceComponent { +export class DatatableDocumentReferenceComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Date', versions: '*', format: 'date', getter: d => d.date }, { title: 'Content', versions: '*', getter: d => d.content?.[0]?.attachment.title }, diff --git a/frontend/src/app/components/list-generic-resource/list-encounter.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-encounter.component.ts similarity index 62% rename from frontend/src/app/components/list-generic-resource/list-encounter.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-encounter.component.ts index 1e3d65d5..556b7356 100644 --- a/frontend/src/app/components/list-generic-resource/list-encounter.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-encounter.component.ts @@ -1,12 +1,12 @@ import {Component, OnChanges, OnInit} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent, ResourceListComponentInterface} from './datatable-generic-resource.component'; @Component({ - selector: 'app-list-encounter', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-encounter', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListEncounterComponent extends ListGenericResourceComponent { +export class DatatableEncounterComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Period', versions: '*', format: 'period', getter: e => e.period }, { title: 'Encounter', versions: '*', format: 'codeableConcept', getter: e => e.type?.[0] }, diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-explanation-of-benefit.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-explanation-of-benefit.component.ts new file mode 100644 index 00000000..104ce810 --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-explanation-of-benefit.component.ts @@ -0,0 +1,11 @@ +import {Component, OnChanges, OnInit} from '@angular/core'; +import {GenericColumnDefn, DatatableGenericResourceComponent, ResourceListComponentInterface} from './datatable-generic-resource.component'; + +@Component({ + selector: 'fhir-datatable-explanation-of-benefit', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] +}) +export class DatatableExplanationOfBenefitComponent extends DatatableGenericResourceComponent { + columnDefinitions: GenericColumnDefn[] = [] +} diff --git a/frontend/src/app/components/list-generic-resource/list-fallback.component.html b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-fallback.component.html similarity index 56% rename from frontend/src/app/components/list-generic-resource/list-fallback.component.html rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-fallback.component.html index 01d5c5e6..33631df4 100644 --- a/frontend/src/app/components/list-generic-resource/list-fallback.component.html +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-fallback.component.html @@ -1,12 +1,4 @@
- - - e.id }, + { title: 'Title', versions: '*', getter: e => e.reasonCode?.[0] }, + ] +} diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.html b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.html new file mode 100644 index 00000000..b1b50d1e --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.html @@ -0,0 +1,21 @@ +
+ + +
diff --git a/frontend/src/app/components/list-patient/list-patient.component.scss b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.scss similarity index 100% rename from frontend/src/app/components/list-patient/list-patient.component.scss rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.scss diff --git a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.spec.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.spec.ts similarity index 61% rename from frontend/src/app/components/list-generic-resource/list-generic-resource.component.spec.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.spec.ts index 7b950786..8e148f81 100644 --- a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.spec.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.spec.ts @@ -1,17 +1,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ListGenericResourceComponent } from './list-generic-resource.component'; -import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import { DatatableGenericResourceComponent } from './datatable-generic-resource.component'; +import {HTTP_CLIENT_TOKEN} from '../../../dependency-injection'; import {HttpClient} from '@angular/common/http'; import {HttpClientTestingModule} from '@angular/common/http/testing'; describe('ListGenericResourceComponent', () => { - let component: ListGenericResourceComponent; - let fixture: ComponentFixture; + let component: DatatableGenericResourceComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ ListGenericResourceComponent ], + declarations: [ DatatableGenericResourceComponent ], imports: [HttpClientTestingModule], providers: [ { @@ -22,7 +22,7 @@ describe('ListGenericResourceComponent', () => { }) .compileComponents(); - fixture = TestBed.createComponent(ListGenericResourceComponent); + fixture = TestBed.createComponent(DatatableGenericResourceComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.ts similarity index 71% rename from frontend/src/app/components/list-generic-resource/list-generic-resource.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.ts index 8d53d522..615c8457 100644 --- a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-generic-resource.component.ts @@ -1,17 +1,22 @@ -import {ChangeDetectorRef, Component, Input, OnInit, ViewChild} from '@angular/core'; +import {ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core'; import {DatatableComponent, ColumnMode, SelectionType} from '@swimlane/ngx-datatable'; -import {ResourceFhir} from '../../models/fasten/resource_fhir'; +import {ResourceFhir} from '../../../models/fasten/resource_fhir'; import {FORMATTERS, getPath, obsValue, attributeXTime} from './utils'; -import {Router} from '@angular/router'; -import {Observable, of} from 'rxjs'; -import {map} from 'rxjs/operators'; -import {FastenApiService} from '../../services/fasten-api.service'; +import {FastenApiService} from '../../../services/fasten-api.service'; +import {FastenDisplayModel} from '../../../../lib/models/fasten/fasten-display-model'; //all Resource list components must implement this Interface export interface ResourceListComponentInterface { + //inputs resourceListType: string; totalElements: number; sourceId: string; + disabledResourceIds: string[]; + + //outputs + selectionChanged: EventEmitter + + //private functions markForCheck() } @@ -30,14 +35,16 @@ class PageInfo { } @Component({ - selector: 'app-list-generic-resource', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-generic-resource', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListGenericResourceComponent implements OnInit, ResourceListComponentInterface { +export class DatatableGenericResourceComponent implements OnInit, ResourceListComponentInterface { @Input() totalElements: number; @Input() resourceListType: string; @Input() sourceId: string; + @Input() disabledResourceIds: string[] = []; + @Output() selectionChanged: EventEmitter = new EventEmitter(); currentPage: PageInfo = {offset: 0} // @Input() resourceList: ResourceFhir[] = [] @@ -53,7 +60,7 @@ export class ListGenericResourceComponent implements OnInit, ResourceListCompone ColumnMode = ColumnMode; SelectionType = SelectionType; - constructor(public changeRef: ChangeDetectorRef, public router: Router, public fastenApi: FastenApiService) { + constructor(public changeRef: ChangeDetectorRef, public fastenApi: FastenApiService) { } @@ -101,6 +108,7 @@ export class ListGenericResourceComponent implements OnInit, ResourceListCompone this.rows = resourceList.map((resource) => { let row = { + resource: resource, source_id: resource.source_id, source_resource_type: resource.source_resource_type, source_resource_id: resource.source_resource_id @@ -125,11 +133,20 @@ export class ListGenericResourceComponent implements OnInit, ResourceListCompone * @param selected */ onSelect({ selected }) { - console.log('Select Event', selected); - this.router.navigateByUrl(`/explore/${selected[0].source_id}/resource/${selected[0].source_resource_id}`); - + this.selectionChanged.emit(selected[0]) } + //check to see if this row should be selectable + // if the row is in the disabled list, it should not be selectable + selectCheck(): (any) => boolean { + return function(row) { + let canSelect = this.disabledResourceIds.indexOf(row.source_resource_id) === -1 + if(!canSelect){ + console.warn(`Row id '${row.source_resource_id}' is disabled, cannot select`) + } + return canSelect + }.bind(this) + } } /////////////////////////////////////////////////////////////////////////////////////// diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-goal.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-goal.component.ts new file mode 100644 index 00000000..043481e8 --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-goal.component.ts @@ -0,0 +1,15 @@ +import {Component, OnChanges, OnInit} from '@angular/core'; +import {GenericColumnDefn, DatatableGenericResourceComponent, ResourceListComponentInterface} from './datatable-generic-resource.component'; + +@Component({ + selector: 'fhir-datatable-goal', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] +}) +export class DatatableGoalComponent extends DatatableGenericResourceComponent { + columnDefinitions: GenericColumnDefn[] = [ + { title: 'Description', versions: '*', getter: e => e.description.text }, + { title: 'Status', versions: '*', getter: e => e.lifecycleStatus }, + { title: 'Status Reason', versions: '*', getter: e => e.statusReason }, + ] +} diff --git a/frontend/src/app/components/list-generic-resource/list-immunization.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-immunization.component.ts similarity index 57% rename from frontend/src/app/components/list-generic-resource/list-immunization.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-immunization.component.ts index 50e10d39..c10cab12 100644 --- a/frontend/src/app/components/list-generic-resource/list-immunization.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-immunization.component.ts @@ -1,12 +1,12 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; @Component({ - selector: 'app-list-immunization', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-immunization', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListImmunizationComponent extends ListGenericResourceComponent { +export class DatatableImmunizationComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Vaccine', versions: '*', format: 'codeableConcept', getter: i => i.vaccineCode }, { title: 'Status', versions: '*', getter: i => i.status }, diff --git a/frontend/src/app/components/list-generic-resource/list-location.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-location.component.ts similarity index 57% rename from frontend/src/app/components/list-generic-resource/list-location.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-location.component.ts index 864e0ffa..1e0016ef 100644 --- a/frontend/src/app/components/list-generic-resource/list-location.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-location.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-location', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-location', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListLocationComponent extends ListGenericResourceComponent { +export class DatatableLocationComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Name', versions: '*', getter: d => d.name || d.alias }, { title: 'Organization', versions: '*', getter: d => d.managingOrganization?.display }, diff --git a/frontend/src/app/components/list-generic-resource/list-medication-administration.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-administration.component.ts similarity index 64% rename from frontend/src/app/components/list-generic-resource/list-medication-administration.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-administration.component.ts index f61ed12e..c017869e 100644 --- a/frontend/src/app/components/list-generic-resource/list-medication-administration.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-administration.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-medication-administration', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-medication-administration', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListMedicationAdministrationComponent extends ListGenericResourceComponent { +export class DatatableMedicationAdministrationComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Medication', versions: '*', format: 'codeableConcept', getter: m => m.medicationCodeableConcept }, { title: 'Route', versions: '*', format: 'codeableConcept', getter: m => m.dosage.route }, diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-dispense.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-dispense.component.ts new file mode 100644 index 00000000..8fce098b --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-dispense.component.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; +import {attributeXTime} from './utils'; + +@Component({ + selector: 'fhir-datatable-medication-dispense', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] +}) +export class DatatableMedicationDispenseComponent extends DatatableGenericResourceComponent { + columnDefinitions: GenericColumnDefn[] = [ + { title: 'Medication', versions: '*', format: 'code', getter: m => m.medicationCodeableConcept?.coding?.[0] }, + { title: 'Handed Over Date', versions: '*', format: 'date', getter: m => m.whenHandedOver} + ] +} diff --git a/frontend/src/app/components/list-generic-resource/list-medication-request.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-request.component.ts similarity index 69% rename from frontend/src/app/components/list-generic-resource/list-medication-request.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-request.component.ts index e967f66f..6041b3b2 100644 --- a/frontend/src/app/components/list-generic-resource/list-medication-request.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication-request.component.ts @@ -1,12 +1,12 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; @Component({ - selector: 'app-list-medication-request', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-medication-request', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListMedicationRequestComponent extends ListGenericResourceComponent { +export class DatatableMedicationRequestComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Author Date', versions: '*', format: 'date', getter: m => m.authoredOn }, { title: 'Medication', versions: '*', format: 'codeableConcept', getter: m => m.medicationCodeableConcept }, //remove drug code diff --git a/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication.component.ts new file mode 100644 index 00000000..65b88dc7 --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-medication.component.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; + +@Component({ + selector: 'fhir-datatable-medication', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] +}) +export class DatatableMedicationComponent extends DatatableGenericResourceComponent { + columnDefinitions: GenericColumnDefn[] = [ + { title: 'Medication', versions: '*', format: 'codeableConcept', getter: c => c.code }, + { title: 'Date Prescribed', versions: '*', format: 'date', getter: c => c.authoredOn }, + { title: 'Status', 'versions': '*', getter: c => c.status } + ] +} diff --git a/frontend/src/app/components/list-generic-resource/list-nutrition-order.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-nutrition-order.component.ts similarity index 58% rename from frontend/src/app/components/list-generic-resource/list-nutrition-order.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-nutrition-order.component.ts index 3c9427a6..92610f87 100644 --- a/frontend/src/app/components/list-generic-resource/list-nutrition-order.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-nutrition-order.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-nutrition-order', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-nutrition-order', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListNutritionOrderComponent extends ListGenericResourceComponent { +export class DatatableNutritionOrderComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Preference', versions: '*', format: 'code', getter: n => n.foodPreferenceModifier?.[0].coding?.[0] }, { title: 'Exclusion', versions: '*', format: 'code', getter: n => n.excludeFoodModifier?.[0].coding?.[0] }, diff --git a/frontend/src/app/components/list-generic-resource/list-observation.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-observation.component.ts similarity index 63% rename from frontend/src/app/components/list-generic-resource/list-observation.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-observation.component.ts index 207d0dbb..323cddad 100644 --- a/frontend/src/app/components/list-generic-resource/list-observation.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-observation.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; import {attributeXTime, obsValue} from './utils'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; @Component({ - selector: 'app-list-observation', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-observation', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListObservationComponent extends ListGenericResourceComponent { +export class DatatableObservationComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Issued Date', 'versions': '*', format: 'date', getter: o => o.issued }, { title: 'Effective', 'versions': '*', getter: o => attributeXTime(o,'effective') }, diff --git a/frontend/src/app/components/list-generic-resource/list-organization.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-organization.component.ts similarity index 52% rename from frontend/src/app/components/list-generic-resource/list-organization.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-organization.component.ts index 13620765..95e45a2c 100644 --- a/frontend/src/app/components/list-generic-resource/list-organization.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-organization.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-organization', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-organization', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListOrganizationComponent extends ListGenericResourceComponent { +export class DatatableOrganizationComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Name', versions: '*', getter: d => d.name || d.alias?.[0] }, { title: 'Address', versions: '*', format: 'address', getter: d => d.address?.[0] }, diff --git a/frontend/src/app/components/list-generic-resource/list-practitioner.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-practitioner.component.ts similarity index 53% rename from frontend/src/app/components/list-generic-resource/list-practitioner.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-practitioner.component.ts index bbb4dfc6..1906d54c 100644 --- a/frontend/src/app/components/list-generic-resource/list-practitioner.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-practitioner.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; import {attributeXTime} from './utils'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; @Component({ - selector: 'app-list-practitioner', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-practitioner', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListPractitionerComponent extends ListGenericResourceComponent { +export class DatatablePractitionerComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Name', versions: '*', format: 'humanName', getter: p => p.name?.[0] }, { title: 'Address', versions: '*', format: 'address', getter: p => p.address?.[0] }, diff --git a/frontend/src/app/components/list-generic-resource/list-procedure.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-procedure.component.ts similarity index 67% rename from frontend/src/app/components/list-generic-resource/list-procedure.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-procedure.component.ts index b60ffe9e..9beaf442 100644 --- a/frontend/src/app/components/list-generic-resource/list-procedure.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-procedure.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; import {attributeXTime} from './utils'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; @Component({ - selector: 'app-list-procedure', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-procedure', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListProcedureComponent extends ListGenericResourceComponent { +export class DatatableProcedureComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ { title: 'Procedure', versions: '*', format: 'codeableConcept', getter: p => p.code }, { title: 'Performed', versions: '*', getter: p => attributeXTime(p,'performed') }, diff --git a/frontend/src/app/components/list-generic-resource/list-service-request.component.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-service-request.component.ts similarity index 73% rename from frontend/src/app/components/list-generic-resource/list-service-request.component.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-service-request.component.ts index f8562a8b..1e3e2c27 100644 --- a/frontend/src/app/components/list-generic-resource/list-service-request.component.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/datatable-service-request.component.ts @@ -1,13 +1,13 @@ import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; +import {GenericColumnDefn, DatatableGenericResourceComponent} from './datatable-generic-resource.component'; import {attributeXTime} from './utils'; @Component({ - selector: 'app-list-service-request', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] + selector: 'fhir-datatable-service-request', + templateUrl: './datatable-generic-resource.component.html', + styleUrls: ['./datatable-generic-resource.component.scss'] }) -export class ListServiceRequestComponent extends ListGenericResourceComponent { +export class DatatableServiceRequestComponent extends DatatableGenericResourceComponent { columnDefinitions: GenericColumnDefn[] = [ // orig { title: 'Service', versions: '*', format: 'code', getter: s => s.code.coding[0] }, { title: 'Author Date', versions: '*', format: 'date', getter: s => s.authoredOn }, diff --git a/frontend/src/app/components/list-generic-resource/utils.ts b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/utils.ts similarity index 98% rename from frontend/src/app/components/list-generic-resource/utils.ts rename to frontend/src/app/components/fhir-datatable/datatable-generic-resource/utils.ts index bb685bcc..23b486d0 100644 --- a/frontend/src/app/components/list-generic-resource/utils.ts +++ b/frontend/src/app/components/fhir-datatable/datatable-generic-resource/utils.ts @@ -36,6 +36,9 @@ export const FORMATTERS = { }, humanName: (humanName) => { if(!humanName) return '' + if(humanName.text){ + return humanName.text + } var nameParts = [] if(humanName.prefix) nameParts.push(humanName.prefix.join(', ')) if(humanName.given) nameParts.push(humanName.given.join(' ')) diff --git a/frontend/src/app/components/fhir-datatable/fhir-datatable.module.ts b/frontend/src/app/components/fhir-datatable/fhir-datatable.module.ts new file mode 100644 index 00000000..7953a01a --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/fhir-datatable.module.ts @@ -0,0 +1,111 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import {DatatableAdverseEventComponent} from './datatable-generic-resource/datatable-adverse-event.component'; +import {DatatableCarePlanComponent} from './datatable-generic-resource/datatable-care-plan.component'; +import {DatatableAppointmentComponent} from './datatable-generic-resource/datatable-appointment.component'; +import {DatatableBinaryComponent} from './datatable-generic-resource/datatable-binary.component'; +import {DatatableAllergyIntoleranceComponent} from './datatable-generic-resource/datatable-allergy-intolerance.component'; +import {DatatableCareTeamComponent} from './datatable-generic-resource/datatable-care-team.component'; +import {DatatableCommunicationComponent} from './datatable-generic-resource/datatable-communication.component'; +import {DatatableConditionComponent} from './datatable-generic-resource/datatable-condition.component'; +import {DatatableCoverageComponent} from './datatable-generic-resource/datatable-coverage.component'; +import {DatatableDeviceComponent} from './datatable-generic-resource/datatable-device.component'; +import {DatatableDeviceRequestComponent} from './datatable-generic-resource/datatable-device-request.component'; +import {DatatableDiagnosticReportComponent} from './datatable-generic-resource/datatable-diagnostic-report.component'; +import {DatatableDocumentReferenceComponent} from './datatable-generic-resource/datatable-document-reference.component'; +import {DatatableEncounterComponent} from './datatable-generic-resource/datatable-encounter.component'; +import {DatatableGenericResourceComponent} from './datatable-generic-resource/datatable-generic-resource.component'; +import {DatatableGoalComponent} from './datatable-generic-resource/datatable-goal.component'; +import {DatatableImmunizationComponent} from './datatable-generic-resource/datatable-immunization.component'; +import {DatatableLocationComponent} from './datatable-generic-resource/datatable-location.component'; +import {DatatableMedicationAdministrationComponent} from './datatable-generic-resource/datatable-medication-administration.component'; +import {DatatableMedicationComponent} from './datatable-generic-resource/datatable-medication.component'; +import {DatatableMedicationDispenseComponent} from './datatable-generic-resource/datatable-medication-dispense.component'; +import {DatatableMedicationRequestComponent} from './datatable-generic-resource/datatable-medication-request.component'; +import {DatatableNutritionOrderComponent} from './datatable-generic-resource/datatable-nutrition-order.component'; +import {DatatableObservationComponent} from './datatable-generic-resource/datatable-observation.component'; +import {DatatableOrganizationComponent} from './datatable-generic-resource/datatable-organization.component'; +import {ListPatientComponent} from './list-patient/list-patient.component'; +import {DatatablePractitionerComponent} from './datatable-generic-resource/datatable-practitioner.component'; +import {DatatableProcedureComponent} from './datatable-generic-resource/datatable-procedure.component'; +import {DatatableServiceRequestComponent} from './datatable-generic-resource/datatable-service-request.component'; +import {FhirDatatableComponent} from './fhir-datatable/fhir-datatable.component'; +import {FhirDatatableOutletDirective} from './fhir-datatable/fhir-datatable-outlet.directive'; +import {DatatableFallbackComponent} from './datatable-generic-resource/datatable-fallback.component'; +import {NgxDatatableModule} from '@swimlane/ngx-datatable'; + +@NgModule({ + imports: [ + CommonModule, + NgxDatatableModule, + ], + declarations: [ + DatatableAdverseEventComponent, + DatatableAllergyIntoleranceComponent, + DatatableAppointmentComponent, + DatatableBinaryComponent, + DatatableCarePlanComponent, + DatatableCareTeamComponent, + DatatableCommunicationComponent, + DatatableConditionComponent, + DatatableCoverageComponent, + DatatableDeviceComponent, + DatatableDeviceRequestComponent, + DatatableDiagnosticReportComponent, + DatatableDocumentReferenceComponent, + DatatableEncounterComponent, + DatatableGenericResourceComponent, + DatatableGoalComponent, + DatatableImmunizationComponent, + DatatableLocationComponent, + DatatableMedicationAdministrationComponent, + DatatableMedicationComponent, + DatatableMedicationDispenseComponent, + DatatableMedicationRequestComponent, + DatatableNutritionOrderComponent, + DatatableObservationComponent, + DatatableOrganizationComponent, + ListPatientComponent, + DatatablePractitionerComponent, + DatatableProcedureComponent, + DatatableServiceRequestComponent, + FhirDatatableComponent, + FhirDatatableOutletDirective, + DatatableFallbackComponent, + ], + exports: [ + DatatableAdverseEventComponent, + DatatableAllergyIntoleranceComponent, + DatatableAppointmentComponent, + DatatableBinaryComponent, + DatatableCarePlanComponent, + DatatableCareTeamComponent, + DatatableCommunicationComponent, + DatatableConditionComponent, + DatatableCoverageComponent, + DatatableDeviceComponent, + DatatableDeviceRequestComponent, + DatatableDiagnosticReportComponent, + DatatableDocumentReferenceComponent, + DatatableEncounterComponent, + DatatableGenericResourceComponent, + DatatableGoalComponent, + DatatableImmunizationComponent, + DatatableLocationComponent, + DatatableMedicationAdministrationComponent, + DatatableMedicationComponent, + DatatableMedicationDispenseComponent, + DatatableMedicationRequestComponent, + DatatableNutritionOrderComponent, + DatatableObservationComponent, + DatatableOrganizationComponent, + ListPatientComponent, + DatatablePractitionerComponent, + DatatableProcedureComponent, + DatatableServiceRequestComponent, + FhirDatatableComponent, + FhirDatatableOutletDirective, + DatatableFallbackComponent, + ] +}) +export class FhirDatatableModule { } diff --git a/frontend/src/app/components/resource-list/resource-list-outlet.directive.spec.ts b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable-outlet.directive.spec.ts similarity index 72% rename from frontend/src/app/components/resource-list/resource-list-outlet.directive.spec.ts rename to frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable-outlet.directive.spec.ts index a33f2e02..9f567800 100644 --- a/frontend/src/app/components/resource-list/resource-list-outlet.directive.spec.ts +++ b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable-outlet.directive.spec.ts @@ -1,4 +1,4 @@ -import { ResourceListOutletDirective } from './resource-list-outlet.directive'; +import { FhirDatatableOutletDirective } from './fhir-datatable-outlet.directive'; // describe('ResourceListOutletDirective', () => { // it('should create an instance', () => { diff --git a/frontend/src/app/components/fhir/fhir-resource/fhir-resource-outlet.directive.ts b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable-outlet.directive.ts similarity index 63% rename from frontend/src/app/components/fhir/fhir-resource/fhir-resource-outlet.directive.ts rename to frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable-outlet.directive.ts index 1a35426e..dea0439a 100644 --- a/frontend/src/app/components/fhir/fhir-resource/fhir-resource-outlet.directive.ts +++ b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable-outlet.directive.ts @@ -1,9 +1,9 @@ import {Directive, ViewContainerRef} from '@angular/core'; @Directive({ - selector: '[fhirResourceOutlet]' + selector: '[fhirDatatableOutlet]' }) -export class FhirResourceOutletDirective { +export class FhirDatatableOutletDirective { constructor(public viewContainerRef: ViewContainerRef) { } diff --git a/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.html b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.html new file mode 100644 index 00000000..3611f67c --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.html @@ -0,0 +1,25 @@ +

{{resourceListType}}

+ + + + + + + + + + diff --git a/frontend/src/app/components/resource-list/resource-list.component.scss b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.scss similarity index 100% rename from frontend/src/app/components/resource-list/resource-list.component.scss rename to frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.scss diff --git a/frontend/src/app/components/resource-list/resource-list.component.spec.ts b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.spec.ts similarity index 56% rename from frontend/src/app/components/resource-list/resource-list.component.spec.ts rename to frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.spec.ts index f4922b2e..fe7d406d 100644 --- a/frontend/src/app/components/resource-list/resource-list.component.spec.ts +++ b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.spec.ts @@ -1,20 +1,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ResourceListComponent } from './resource-list.component'; +import { FhirDatatableComponent } from './fhir-datatable.component'; import {HttpClientTestingModule} from '@angular/common/http/testing'; -import {ResourceListOutletDirective} from './resource-list-outlet.directive'; -import {FastenApiService} from '../../services/fasten-api.service'; -import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {FhirDatatableOutletDirective} from './fhir-datatable-outlet.directive'; +import {FastenApiService} from '../../../services/fasten-api.service'; +import {HTTP_CLIENT_TOKEN} from '../../../dependency-injection'; import {HttpClient} from '@angular/common/http'; describe('ResourceListComponent', () => { - let component: ResourceListComponent; - let fixture: ComponentFixture; + let component: FhirDatatableComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [HttpClientTestingModule], - declarations: [ ResourceListComponent, ResourceListOutletDirective ], + declarations: [ FhirDatatableComponent, FhirDatatableOutletDirective ], providers: [ FastenApiService, { @@ -25,7 +25,7 @@ describe('ResourceListComponent', () => { }) .compileComponents(); - fixture = TestBed.createComponent(ResourceListComponent); + fixture = TestBed.createComponent(FhirDatatableComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.ts b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.ts new file mode 100644 index 00000000..15383f41 --- /dev/null +++ b/frontend/src/app/components/fhir-datatable/fhir-datatable/fhir-datatable.component.ts @@ -0,0 +1,182 @@ +import {ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, SimpleChanges, Type, ViewChild} from '@angular/core'; +import {FastenApiService} from '../../../services/fasten-api.service'; +import {Source} from '../../../models/fasten/source'; +import {DatatableAdverseEventComponent} from '../datatable-generic-resource/datatable-adverse-event.component'; +import {DatatableAllergyIntoleranceComponent} from '../datatable-generic-resource/datatable-allergy-intolerance.component'; +import {DatatableAppointmentComponent} from '../datatable-generic-resource/datatable-appointment.component'; +import {DatatableBinaryComponent} from '../datatable-generic-resource/datatable-binary.component'; +import {DatatableCarePlanComponent} from '../datatable-generic-resource/datatable-care-plan.component'; +import {DatatableCareTeamComponent} from '../datatable-generic-resource/datatable-care-team.component'; +import {DatatableCommunicationComponent} from '../datatable-generic-resource/datatable-communication.component'; +import {DatatableConditionComponent} from '../datatable-generic-resource/datatable-condition.component'; +import {DatatableCoverageComponent} from '../datatable-generic-resource/datatable-coverage.component'; +import {DatatableDeviceComponent} from '../datatable-generic-resource/datatable-device.component'; +import {DatatableDeviceRequestComponent} from '../datatable-generic-resource/datatable-device-request.component'; +import {DatatableDiagnosticReportComponent} from '../datatable-generic-resource/datatable-diagnostic-report.component'; +import {DatatableDocumentReferenceComponent} from '../datatable-generic-resource/datatable-document-reference.component'; +import {DatatableEncounterComponent} from '../datatable-generic-resource/datatable-encounter.component'; +import {DatatableFallbackComponent} from '../datatable-generic-resource/datatable-fallback.component'; +import {DatatableGenericResourceComponent, ResourceListComponentInterface} from '../datatable-generic-resource/datatable-generic-resource.component'; +import {DatatableGoalComponent} from '../datatable-generic-resource/datatable-goal.component'; +import {DatatableImmunizationComponent} from '../datatable-generic-resource/datatable-immunization.component'; +import {DatatableLocationComponent} from '../datatable-generic-resource/datatable-location.component'; +import {DatatableMedicationAdministrationComponent} from '../datatable-generic-resource/datatable-medication-administration.component'; +import {DatatableMedicationComponent} from '../datatable-generic-resource/datatable-medication.component'; +import {DatatableMedicationDispenseComponent} from '../datatable-generic-resource/datatable-medication-dispense.component'; +import {DatatableMedicationRequestComponent} from '../datatable-generic-resource/datatable-medication-request.component'; +import {DatatableNutritionOrderComponent} from '../datatable-generic-resource/datatable-nutrition-order.component'; +import {DatatableObservationComponent} from '../datatable-generic-resource/datatable-observation.component'; +import {DatatableOrganizationComponent} from '../datatable-generic-resource/datatable-organization.component'; +import {DatatablePractitionerComponent} from '../datatable-generic-resource/datatable-practitioner.component'; +import {DatatableProcedureComponent} from '../datatable-generic-resource/datatable-procedure.component'; +import {DatatableServiceRequestComponent} from '../datatable-generic-resource/datatable-service-request.component'; +import {FhirDatatableOutletDirective} from './fhir-datatable-outlet.directive'; +import {Router} from '@angular/router'; +import {FastenDisplayModel} from '../../../../lib/models/fasten/fasten-display-model'; + +@Component({ + selector: 'fhir-datatable', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './fhir-datatable.component.html', + styleUrls: ['./fhir-datatable.component.scss'] +}) +export class FhirDatatableComponent implements OnInit, OnChanges { + + @Input() source: Source; + @Input() resourceListType: string; + @Input() selectedTotalElements: number; + @Input() disabledResourceIds: string[] = []; + + //location to dynamically load the resource list + @ViewChild(FhirDatatableOutletDirective, {static: true}) resourceListOutlet!: FhirDatatableOutletDirective; + + knownResourceType: boolean = true; + + constructor(public router: Router, private fastenApi: FastenApiService) { } + + ngOnInit(): void { + this.loadComponent() + } + ngOnChanges(changes: SimpleChanges) { + this.loadComponent() + } + + loadComponent() { + //clear the current outlet + const viewContainerRef = this.resourceListOutlet.viewContainerRef; + viewContainerRef.clear(); + + let componentType = this.typeLookup(this.resourceListType) + if(componentType != null){ + console.log("Attempting to create component", this.resourceListType, componentType) + const componentRef = viewContainerRef.createComponent(componentType); + componentRef.instance.totalElements = this.selectedTotalElements; + componentRef.instance.resourceListType = this.resourceListType; + componentRef.instance.sourceId = this.source.id; + componentRef.instance.markForCheck() + if(this.disabledResourceIds){ + componentRef.instance.disabledResourceIds = this.disabledResourceIds + } + + componentRef.instance.selectionChanged.subscribe((selected: FastenDisplayModel) => { + this.router.navigateByUrl(`/explore/${selected?.source_id}/resource/${selected?.source_resource_id}`); + }) + this.knownResourceType = (componentType != DatatableFallbackComponent) + } + } + + typeLookup(resourceType: string): Type { + if(!resourceType){ + //dont try to render anything if the resourceType isnt set. + return null + } + switch(resourceType) { + case "Appointment": { + return DatatableAppointmentComponent; + } + case "AllergyIntolerance": { + return DatatableAllergyIntoleranceComponent; + } + case "AdverseEvent": { + return DatatableAdverseEventComponent; + } + case "Binary": { + return DatatableBinaryComponent; + } + case "CarePlan": { + return DatatableCarePlanComponent; + } + case "CareTeam": { + return DatatableCareTeamComponent; + } + case "Communication": { + return DatatableCommunicationComponent; + } + case "Condition": { + return DatatableConditionComponent; + } + case "Coverage": { + return DatatableCoverageComponent; + } + case "Device": { + return DatatableDeviceComponent; + } + case "DeviceRequest": { + return DatatableDeviceRequestComponent; + } + case "DiagnosticReport": { + return DatatableDiagnosticReportComponent; + } + case "DocumentReference": { + return DatatableDocumentReferenceComponent; + } + case "Encounter": { + return DatatableEncounterComponent; + } + case "Goal": { + return DatatableGoalComponent; + } + case "Immunization": { + return DatatableImmunizationComponent; + } + case "Location": { + return DatatableLocationComponent; + } + case "Medication": { + return DatatableMedicationComponent; + } + case "MedicationAdministration": { + return DatatableMedicationAdministrationComponent; + } + case "MedicationDispense": { + return DatatableMedicationDispenseComponent; + } + case "MedicationRequest": { + return DatatableMedicationRequestComponent; + } + case "NutritionOrder": { + return DatatableNutritionOrderComponent; + } + case "Observation": { + return DatatableObservationComponent; + } + case "Organization": { + return DatatableOrganizationComponent; + } + case "Practitioner": { + return DatatablePractitionerComponent; + } + case "Procedure": { + return DatatableProcedureComponent; + } + case "ServiceRequest": { + return DatatableServiceRequestComponent; + } + default: { + console.warn("Unknown component type, using fallback", resourceType) + return DatatableFallbackComponent; + + } + } + } +} diff --git a/frontend/src/app/components/list-patient/list-patient.component.html b/frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.html similarity index 100% rename from frontend/src/app/components/list-patient/list-patient.component.html rename to frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.html diff --git a/frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.scss b/frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/list-patient/list-patient.component.spec.ts b/frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.spec.ts similarity index 100% rename from frontend/src/app/components/list-patient/list-patient.component.spec.ts rename to frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.spec.ts diff --git a/frontend/src/app/components/list-patient/list-patient.component.ts b/frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.ts similarity index 100% rename from frontend/src/app/components/list-patient/list-patient.component.ts rename to frontend/src/app/components/fhir-datatable/list-patient/list-patient.component.ts diff --git a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.html b/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.html deleted file mode 100644 index b9aabc6a..00000000 --- a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.spec.ts b/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.spec.ts deleted file mode 100644 index 69a5e679..00000000 --- a/frontend/src/app/components/fhir/fhir-resource/fhir-resource.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { FhirResourceComponent } from './fhir-resource.component'; -import {FhirResourceOutletDirective} from './fhir-resource-outlet.directive'; - -describe('FhirResourceComponent', () => { - let component: FhirResourceComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ FhirResourceComponent, FhirResourceOutletDirective ], - }) - .compileComponents(); - - fixture = TestBed.createComponent(FhirResourceComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/components/list-generic-resource/list-adverse-event.component.ts b/frontend/src/app/components/list-generic-resource/list-adverse-event.component.ts deleted file mode 100644 index 0ab0c02c..00000000 --- a/frontend/src/app/components/list-generic-resource/list-adverse-event.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; -import {attributeXTime} from './utils'; - -@Component({ - selector: 'app-list-adverse-event', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] -}) -export class ListAdverseEventComponent extends ListGenericResourceComponent { - columnDefinitions: GenericColumnDefn[] = [ - { title: 'Event', versions: '*', format: 'codeableConcept', getter: a => a.event }, - { title: 'Date', versions: '*', format: 'date', getter: a => a.date } - ] -} diff --git a/frontend/src/app/components/list-generic-resource/list-coverage.component.ts b/frontend/src/app/components/list-generic-resource/list-coverage.component.ts deleted file mode 100644 index 222ae070..00000000 --- a/frontend/src/app/components/list-generic-resource/list-coverage.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; -import {attributeXTime} from './utils'; - -@Component({ - selector: 'app-list-coverage', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] -}) -export class ListCoverageComponent extends ListGenericResourceComponent { - columnDefinitions: GenericColumnDefn[] = [ - { title: 'Type', versions: '*', format: 'codeableConcept', getter: c => c.type }, - { title: 'Period', versions: '*', format: 'period', getter: c => c.period } - ] -} diff --git a/frontend/src/app/components/list-generic-resource/list-explanation-of-benefit.component.ts b/frontend/src/app/components/list-generic-resource/list-explanation-of-benefit.component.ts deleted file mode 100644 index 26be1b77..00000000 --- a/frontend/src/app/components/list-generic-resource/list-explanation-of-benefit.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {Component, OnChanges, OnInit} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component'; - -@Component({ - selector: 'app-list-explanation-of-benefit', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] -}) -export class ListExplanationOfBenefitComponent extends ListGenericResourceComponent { - columnDefinitions: GenericColumnDefn[] = [] -} diff --git a/frontend/src/app/components/list-generic-resource/list-fallback.component.ts b/frontend/src/app/components/list-generic-resource/list-fallback.component.ts deleted file mode 100644 index 17d497e1..00000000 --- a/frontend/src/app/components/list-generic-resource/list-fallback.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {Component, OnChanges, OnInit} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component'; - -@Component({ - selector: 'app-list-fallback', - templateUrl: './list-fallback.component.html', - styleUrls: ['./list-generic-resource.component.scss'] -}) -export class ListFallbackComponent extends ListGenericResourceComponent { - columnDefinitions: GenericColumnDefn[] = [ - { title: 'Id', versions: '*', getter: e => e.id }, - { title: 'Title', versions: '*', getter: e => e.reasonCode?.[0] }, - ] -} diff --git a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.html b/frontend/src/app/components/list-generic-resource/list-generic-resource.component.html deleted file mode 100644 index 1d19695c..00000000 --- a/frontend/src/app/components/list-generic-resource/list-generic-resource.component.html +++ /dev/null @@ -1,32 +0,0 @@ -
- - - - - -
diff --git a/frontend/src/app/components/list-generic-resource/list-goal.component.ts b/frontend/src/app/components/list-generic-resource/list-goal.component.ts deleted file mode 100644 index fa67abad..00000000 --- a/frontend/src/app/components/list-generic-resource/list-goal.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {Component, OnChanges, OnInit} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent, ResourceListComponentInterface} from './list-generic-resource.component'; - -@Component({ - selector: 'app-list-goal', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] -}) -export class ListGoalComponent extends ListGenericResourceComponent { - columnDefinitions: GenericColumnDefn[] = [ - { title: 'Description', versions: '*', getter: e => e.description.text }, - { title: 'Status', versions: '*', getter: e => e.lifecycleStatus }, - { title: 'Status Reason', versions: '*', getter: e => e.statusReason }, - ] -} diff --git a/frontend/src/app/components/list-generic-resource/list-medication-dispense.component.ts b/frontend/src/app/components/list-generic-resource/list-medication-dispense.component.ts deleted file mode 100644 index c617a203..00000000 --- a/frontend/src/app/components/list-generic-resource/list-medication-dispense.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; -import {attributeXTime} from './utils'; - -@Component({ - selector: 'app-list-medication-dispense', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] -}) -export class ListMedicationDispenseComponent extends ListGenericResourceComponent { - columnDefinitions: GenericColumnDefn[] = [ - { title: 'Medication', versions: '*', format: 'code', getter: m => m.medicationCodeableConcept?.coding?.[0] }, - { title: 'Handed Over Date', versions: '*', format: 'date', getter: m => m.whenHandedOver} - ] -} diff --git a/frontend/src/app/components/list-generic-resource/list-medication.component.ts b/frontend/src/app/components/list-generic-resource/list-medication.component.ts deleted file mode 100644 index 11ddbfd5..00000000 --- a/frontend/src/app/components/list-generic-resource/list-medication.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {Component} from '@angular/core'; -import {GenericColumnDefn, ListGenericResourceComponent} from './list-generic-resource.component'; - -@Component({ - selector: 'app-list-medication', - templateUrl: './list-generic-resource.component.html', - styleUrls: ['./list-generic-resource.component.scss'] -}) -export class ListMedicationComponent extends ListGenericResourceComponent { - columnDefinitions: GenericColumnDefn[] = [ - { title: 'Medication', versions: '*', format: 'codeableConcept', getter: c => c.code }, - { title: 'Date Prescribed', versions: '*', format: 'date', getter: c => c.authoredOn }, - { title: 'Status', 'versions': '*', getter: c => c.status } - ] -} diff --git a/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.html b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.html new file mode 100644 index 00000000..c086344c --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.html @@ -0,0 +1,40 @@ + + + diff --git a/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.scss b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.spec.ts b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.spec.ts new file mode 100644 index 00000000..c133f7e3 --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.spec.ts @@ -0,0 +1,32 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MedicalRecordWizardAddAttachmentComponent } from './medical-record-wizard-add-attachment.component'; +import {NgbActiveModal, NgbModal, NgbModalModule} from '@ng-bootstrap/ng-bootstrap'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; + +describe('MedicalRecordWizardAddAttachmentComponent', () => { + let component: MedicalRecordWizardAddAttachmentComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + providers: [ NgbModal, NgbActiveModal, { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + } ], + imports: [ MedicalRecordWizardAddAttachmentComponent, HttpClientTestingModule ], + declarations: [ ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MedicalRecordWizardAddAttachmentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.ts b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.ts new file mode 100644 index 00000000..ed39600b --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component.ts @@ -0,0 +1,67 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from '@angular/forms'; +import {NlmTypeaheadComponent} from '../nlm-typeahead/nlm-typeahead.component'; +import {HighlightModule} from 'ngx-highlightjs'; +import {NgbActiveModal, NgbTooltipModule} from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + NlmTypeaheadComponent, + HighlightModule, + NgbTooltipModule + ], + selector: 'app-medical-record-wizard-add-attachment', + templateUrl: './medical-record-wizard-add-attachment.component.html', + styleUrls: ['./medical-record-wizard-add-attachment.component.scss'] +}) +export class MedicalRecordWizardAddAttachmentComponent implements OnInit { + @Input() debugMode: boolean = false; + + newAttachmentForm: FormGroup + + constructor(public activeModal: NgbActiveModal) { } + + ngOnInit(): void { + this.resetAttachmentForm() + } + + submit() { + this.newAttachmentForm.markAllAsTouched() + if(this.newAttachmentForm.valid){ + this.activeModal.close(this.newAttachmentForm.getRawValue()); + } + } + + onAttachmentFileChange($event){ + console.log("onAttachmentFileChange") + let fileInput = $event.target as HTMLInputElement; + if (fileInput.files && fileInput.files[0]) { + let reader = new FileReader(); + reader.onloadend = () => { + // use a regex to remove data url part + const base64String = (reader.result as string).replace('data:', '').replace(/^.+,/, ''); + this.newAttachmentForm.get('file_content').setValue(base64String) + }; + reader.readAsDataURL(fileInput.files[0]); + this.newAttachmentForm.get('file_name').setValue(fileInput.files[0].name) + this.newAttachmentForm.get('file_size').setValue(fileInput.files[0].size) + } + } + + private resetAttachmentForm(){ + + this.newAttachmentForm = new FormGroup({ + name: new FormControl(null, Validators.required), + category: new FormControl(null, Validators.required), + file_type: new FormControl(null, Validators.required), + file_name: new FormControl(null, Validators.required), + file_content: new FormControl(null, Validators.required), + file_size: new FormControl(null), + }) + } +} diff --git a/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.stories.ts b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.stories.ts new file mode 100644 index 00000000..26fc0aab --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.stories.ts @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {MedicalRecordWizardAddAttachmentComponent} from './medical-record-wizard-add-attachment.component'; +import {applicationConfig} from '@storybook/angular'; +import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {importProvidersFrom} from '@angular/core'; + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Components/MedicalRecordWizardAddAttachment', + component: MedicalRecordWizardAddAttachmentComponent, + decorators: [ + applicationConfig({ + providers: [ + importProvidersFrom(HttpClientTestingModule), + NgbActiveModal, + { + provide: HttpClient, + useClass: HttpClient + }, + { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + } + ] + }), + ], + tags: ['autodocs'], + render: (args: MedicalRecordWizardAddAttachmentComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +export const Primary: Story = {}; + diff --git a/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.html b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.html new file mode 100644 index 00000000..35b3d017 --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.html @@ -0,0 +1,76 @@ + + + diff --git a/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.scss b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.spec.ts b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.spec.ts new file mode 100644 index 00000000..53d00a80 --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MedicalRecordWizardAddEncounterComponent } from './medical-record-wizard-add-encounter.component'; +import {NgbActiveModal, NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; + +describe('MedicalRecordWizardAddEncounterComponent', () => { + let component: MedicalRecordWizardAddEncounterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ MedicalRecordWizardAddEncounterComponent, HttpClientTestingModule ], + providers: [NgbModal, NgbActiveModal, { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + }] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MedicalRecordWizardAddEncounterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.ts b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.ts new file mode 100644 index 00000000..a9cb57a0 --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component.ts @@ -0,0 +1,158 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from '@angular/forms'; +import {NlmTypeaheadComponent} from '../nlm-typeahead/nlm-typeahead.component'; +import {HighlightModule} from 'ngx-highlightjs'; +import {NgbActiveModal, NgbDatepickerModule, NgbNavModule, NgbTooltipModule} from '@ng-bootstrap/ng-bootstrap'; +import {FhirDatatableModule} from '../fhir-datatable/fhir-datatable.module'; +import {ResourceType} from '../../../lib/models/constants'; +import {ResourceFhir} from '../../models/fasten/resource_fhir'; +import {FastenApiService} from '../../services/fasten-api.service'; +import {ResponseWrapper} from '../../models/response-wrapper'; +import {fhirModelFactory} from '../../../lib/models/factory'; +import {RecResourceRelatedDisplayModel} from '../../../lib/utils/resource_related_display_model'; +import {EncounterModel} from '../../../lib/models/resources/encounter-model'; +import {uuidV4} from '../../../lib/utils/uuid'; + +@Component({ + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + NlmTypeaheadComponent, + HighlightModule, + NgbTooltipModule, + NgbNavModule, + FhirDatatableModule, + NgbDatepickerModule + ], + selector: 'app-medical-record-wizard-add-encounter', + templateUrl: './medical-record-wizard-add-encounter.component.html', + styleUrls: ['./medical-record-wizard-add-encounter.component.scss'] +}) +export class MedicalRecordWizardAddEncounterComponent implements OnInit { + + @Input() debugMode: boolean = false; + loading: boolean = false + activeId: string = 'find' + + //create tab options + newEncounterForm: FormGroup //ResourceCreateEncounter + + //find tab options + selectedEncounter: {source_id: string, source_resource_id: string,source_resource_type: ResourceType, resource: ResourceFhir} = null + totalEncounters: number = 0 + constructor( + public activeModal: NgbActiveModal, + private fastenApi: FastenApiService, + ) { } + + ngOnInit(): void { + this.resetEncounterForm() + + //get a count of all the known organizations + this.fastenApi.queryResources({ + "select": [], + "from": "Encounter", + "where": {}, + "aggregations": { + "count_by": {"field": "*"} + } + }).subscribe((resp: ResponseWrapper) => { + this.totalEncounters = resp.data?.[0].value + }) + } + + changeTab(id: string) { + if(this.activeId != id){ + this.activeId = id + this.resetEncounterForm() + this.selectedEncounter = null + } + } + selectionChanged(event) { + console.log("SELECTION CHANGED", event) + this.selectedEncounter = event + } + get submitEnabled() { + return (this.activeId == 'create' && this.newEncounterForm.valid) || + (this.activeId == 'find' && this.selectedEncounter != null) + } + + submit() { + if(this.activeId == 'create'){ + this.newEncounterForm.markAllAsTouched() + if(this.newEncounterForm.valid){ + this.activeModal.close({ + action: this.activeId, + data: this.encounterFormToDisplayModel(this.newEncounterForm) + }); + } + } else if(this.activeId == 'find'){ + if(this.selectedEncounter == null){ + return + } + + //get all the related resources for the selected encounter + this.loading = true + this.fastenApi.getResourceGraph(null, [{ + source_resource_type: this.selectedEncounter.source_resource_type, + source_resource_id: this.selectedEncounter.source_resource_id, + source_id: this.selectedEncounter.source_id, + }]).subscribe((graphResponse) => { + this.loading = false + + if(graphResponse.results["Encounter"]?.[0]){ + + let parsed = RecResourceRelatedDisplayModel(graphResponse.results["Encounter"]?.[0]) + let encounterDisplayModelWithRelated = parsed.displayModel as EncounterModel + + console.log("Found encounter (and related resources)", encounterDisplayModelWithRelated) + this.activeModal.close({ + action: this.activeId, + data: encounterDisplayModelWithRelated + }); + } else { + console.warn("No encounter found in graph response, falling back to selected encounter", graphResponse) + this.activeModal.close({ + action: this.activeId, + data: fhirModelFactory(this.selectedEncounter.source_resource_type, this.selectedEncounter.resource) + }); + } + }, (err) => { + this.loading = false + }) + } + } + + private encounterFormToDisplayModel(form: FormGroup): EncounterModel { + let encounter = new EncounterModel({}) + encounter.code = form.get('code').value + + if(form.get('period_start').value){ + let period_start = form.get('period_start').value + encounter.period_start = (new Date(period_start.year, period_start.month-1, period_start.day)).toISOString() + } + if(form.get('period_end').value){ + let period_end = form.get('period_end').value + encounter.period_end = (new Date(period_end.year, period_end.month-1, period_end.day)).toISOString() + } + if(!encounter.source_resource_id){ + encounter.source_resource_id = uuidV4(); + } + return encounter + } + + private resetEncounterForm(){ + + this.newEncounterForm = new FormGroup({ + id: new FormControl(null), + identifier: new FormControl([]), + code: new FormControl(null, Validators.required), + period_start: new FormControl(null, Validators.required), + period_end: new FormControl(null), + }) + + } +} diff --git a/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.stories.ts b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.stories.ts new file mode 100644 index 00000000..341f097e --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.stories.ts @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {MedicalRecordWizardAddEncounterComponent} from './medical-record-wizard-add-encounter.component'; +import {applicationConfig} from '@storybook/angular'; +import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {importProvidersFrom} from '@angular/core'; + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Components/MedicalRecordWizardAddEncounter', + component: MedicalRecordWizardAddEncounterComponent, + decorators: [ + applicationConfig({ + providers: [ + importProvidersFrom(HttpClientTestingModule), + NgbActiveModal, + { + provide: HttpClient, + useClass: HttpClient + }, + { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + } + ] + }), + ], + tags: ['autodocs'], + render: (args: MedicalRecordWizardAddEncounterComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +export const Primary: Story = {}; + diff --git a/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.html b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.html new file mode 100644 index 00000000..60c59b7d --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.html @@ -0,0 +1,108 @@ + + + diff --git a/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.scss b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.spec.ts b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.spec.ts new file mode 100644 index 00000000..9ac412cc --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MedicalRecordWizardAddOrganizationComponent } from './medical-record-wizard-add-organization.component'; +import {NgbActiveModal, NgbModal, NgbModalModule} from '@ng-bootstrap/ng-bootstrap'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; + +describe('MedicalRecordWizardAddOrganizationComponent', () => { + let component: MedicalRecordWizardAddOrganizationComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + providers: [NgbModal, NgbActiveModal, { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + }], + imports: [ MedicalRecordWizardAddOrganizationComponent, HttpClientTestingModule ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MedicalRecordWizardAddOrganizationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.ts b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.ts new file mode 100644 index 00000000..28aa7dd4 --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.component.ts @@ -0,0 +1,232 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from '@angular/forms'; +import {NlmTypeaheadComponent} from '../nlm-typeahead/nlm-typeahead.component'; +import {HighlightModule} from 'ngx-highlightjs'; +import {NgbActiveModal, NgbNavLink, NgbNavModule, NgbTooltipModule} from '@ng-bootstrap/ng-bootstrap'; +import {FhirDatatableModule} from '../fhir-datatable/fhir-datatable.module'; +import {FastenDisplayModel} from '../../../lib/models/fasten/fasten-display-model'; +import {FastenApiService} from '../../services/fasten-api.service'; +import {ResponseWrapper} from '../../models/response-wrapper'; +import {OrganizationModel} from '../../../lib/models/resources/organization-model'; +import {CodingModel} from '../../../lib/models/datatypes/coding-model'; +import {AddressModel} from '../../../lib/models/datatypes/address-model'; +import {CodableConceptModel} from '../../../lib/models/datatypes/codable-concept-model'; +import {uuidV4} from '../../../lib/utils/uuid'; +import {fhirModelFactory} from '../../../lib/models/factory'; +import {ResourceFhir} from '../../models/fasten/resource_fhir'; +import {ResourceType} from '../../../lib/models/constants'; + +@Component({ + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + NlmTypeaheadComponent, + HighlightModule, + NgbTooltipModule, + NgbNavModule, + FhirDatatableModule + ], + selector: 'app-medical-record-wizard-add-organization', + templateUrl: './medical-record-wizard-add-organization.component.html', + styleUrls: ['./medical-record-wizard-add-organization.component.scss'] +}) +export class MedicalRecordWizardAddOrganizationComponent implements OnInit { + @Input() debugMode: boolean = false; + @Input() disabledResourceIds: string[] = []; + + activeId: string = 'find' + + //create tab options + newOrganizationTypeaheadForm: FormGroup + newOrganizationForm: FormGroup //ResourceCreateOrganization + + //find tab options + selectedOrganization: {source_resource_id: string,source_resource_type: ResourceType, resource: ResourceFhir} = null + totalOrganizations: number = 0 + constructor( + public activeModal: NgbActiveModal, + private fastenApi: FastenApiService, + ) { } + + ngOnInit(): void { + this.resetOrganizationForm() + + //get a count of all the known organizations + this.fastenApi.queryResources({ + "select": [], + "from": "Organization", + "where": {}, + "aggregations": { + "count_by": {"field": "*"} + } + }).subscribe((resp: ResponseWrapper) => { + this.totalOrganizations = resp.data?.[0].value + }) + } + changeTab(id: string) { + if(this.activeId != id){ + this.activeId = id + this.resetOrganizationForm() + this.selectedOrganization = null + } + } + selectionChanged(event) { + console.log("SELECTION CHANGED", event) + this.selectedOrganization = event + } + get submitEnabled() { + return (this.activeId == 'create' && this.newOrganizationForm.valid) || + (this.activeId == 'find' && this.selectedOrganization != null) + } + + submit() { + if(this.activeId == 'create'){ + this.newOrganizationForm.markAllAsTouched() + if(this.newOrganizationForm.valid){ + this.activeModal.close({ + action: this.activeId, + data: this.organizationFormToDisplayModel(this.newOrganizationForm) + }); + } + } else if(this.activeId == 'find'){ + if(this.selectedOrganization != null){ + this.activeModal.close({ + action: this.activeId, + data: fhirModelFactory(this.selectedOrganization.source_resource_type, this.selectedOrganization.resource) + }); + } + } + } + + private organizationFormToDisplayModel(form: FormGroup): OrganizationModel { + let address = new AddressModel(null) + address.city = form.get('address').get('city').value + address.line = [ + form.get('address').get('line1').value, + form.get('address').get('line2').value, + ] + address.state = form.get('address').get('state').value + address.country = form.get('address').get('country').value + address.postalCode = form.get('address').get('zip').value + + let model = new OrganizationModel({}) + model.source_resource_id = form.get('id').value + model.identifier = form.get('identifier').value + model.name = form.get('name').value + model.addresses = [address] + model.telecom = [] + model.type = [] + if (form.get('phone').value) { + model.telecom.push({ + system: 'phone', + value: form.get('phone').value, + use: 'work' + }) + } + if(form.get('fax').value) { + model.telecom.push({ + system: 'fax', + value: form.get('fax').value, + use: 'work' + }) + } + if(form.get('email').value) { + model.telecom.push({ + system: 'email', + value: form.get('email').value, + use: 'work' + }) + } + if(form.get('type').value) { + let codableConcept = new CodableConceptModel({}) + codableConcept.coding = form.get('type').value.identifier + codableConcept.text = form.get('type').value.text + model.type.push(codableConcept) + } + + if(!model.source_resource_id){ + console.warn("No source_resource_id set for Organization, generating one") + model.source_resource_id = uuidV4(); + } + + return model + } + + private resetOrganizationForm(){ + this.newOrganizationTypeaheadForm = new FormGroup({ + data: new FormControl(null, Validators.required), + }) + this.newOrganizationTypeaheadForm.valueChanges.subscribe(form => { + console.log("CHANGE Organization IN MODAL", form) + let val = form.data + + if(val == null){ + //reset the dependant fields (user cleared the text box) + this.newOrganizationForm.get('id').setValue(null) + this.newOrganizationForm.get('type').setValue(null) + this.newOrganizationForm.get('identifier').setValue(null); + this.newOrganizationForm.get('phone').setValue(null); + this.newOrganizationForm.get('fax').setValue(null); + let addressGroup = this.newOrganizationForm.get('address') + addressGroup.get('line1').setValue(null) + addressGroup.get('line2').setValue(null) + addressGroup.get('city').setValue(null) + addressGroup.get('state').setValue(null) + addressGroup.get('zip').setValue(null) + addressGroup.get('country').setValue(null) + this.newOrganizationForm.get('name').setValue(null); + return + } + + if(val.id){ + this.newOrganizationForm.get('id').setValue(val.id) + } + if(val.provider_type) { + this.newOrganizationForm.get('type').setValue(val.provider_type) + } + if(val.identifier){ + this.newOrganizationForm.get('identifier').setValue(val.identifier) + } + if(val.provider_phone){ + this.newOrganizationForm.get('phone').setValue(val.provider_phone) + } + if(val.provider_fax){ + this.newOrganizationForm.get('fax').setValue(val.provider_fax) + } + if(val.provider_address){ + let addressGroup = this.newOrganizationForm.get('address') + addressGroup.get('line1').setValue(val.provider_address.line1) + addressGroup.get('line2').setValue(val.provider_address.line2) + addressGroup.get('city').setValue(val.provider_address.city) + addressGroup.get('state').setValue(val.provider_address.state) + addressGroup.get('zip').setValue(val.provider_address.zip) + addressGroup.get('country').setValue(val.provider_address.country) + } + if(val.text) { + this.newOrganizationForm.get('name').setValue(val.text) + } + }); + + this.newOrganizationForm = new FormGroup({ + id: new FormControl(null), + identifier: new FormControl([]), + name: new FormControl(null, Validators.required), + type: new FormControl(null, Validators.required), + phone: new FormControl(null, Validators.pattern('[- +()0-9]+')), + fax: new FormControl(null, Validators.pattern('[- +()0-9]+')), + email: new FormControl(null, Validators.email), + address: new FormGroup({ + line1: new FormControl(null), + line2: new FormControl(null), + city: new FormControl(null), + state: new FormControl(null), + zip: new FormControl(null), + country: new FormControl(null), + }) + }) + + } +} diff --git a/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.stories.ts b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.stories.ts new file mode 100644 index 00000000..17f36f1f --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-organization/medical-record-wizard-add-organization.stories.ts @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {MedicalRecordWizardAddOrganizationComponent} from './medical-record-wizard-add-organization.component'; +import {applicationConfig} from '@storybook/angular'; +import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {importProvidersFrom} from '@angular/core'; + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Components/MedicalRecordWizardAddOrganization', + component: MedicalRecordWizardAddOrganizationComponent, + decorators: [ + applicationConfig({ + providers: [ + importProvidersFrom(HttpClientTestingModule), + NgbActiveModal, + { + provide: HttpClient, + useClass: HttpClient + }, + { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + } + ] + }), + ], + tags: ['autodocs'], + render: (args: MedicalRecordWizardAddOrganizationComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +export const Primary: Story = {}; + diff --git a/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.html b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.html new file mode 100644 index 00000000..4d5cab9f --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.html @@ -0,0 +1,110 @@ + + + diff --git a/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.scss b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.spec.ts b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.spec.ts new file mode 100644 index 00000000..00f9a9c0 --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MedicalRecordWizardAddPractitionerComponent } from './medical-record-wizard-add-practitioner.component'; +import {NgbActiveModal, NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; + +describe('MedicalRecordWizardAddPractitionerComponent', () => { + let component: MedicalRecordWizardAddPractitionerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ MedicalRecordWizardAddPractitionerComponent, HttpClientTestingModule ], + providers: [ NgbActiveModal, NgbModal, { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + } ], + }) + .compileComponents(); + + fixture = TestBed.createComponent(MedicalRecordWizardAddPractitionerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.ts b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.ts new file mode 100644 index 00000000..f115129e --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component.ts @@ -0,0 +1,241 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {NgbActiveModal, NgbDatepickerModule, NgbNavModule, NgbTooltipModule, NgbTypeaheadModule} from '@ng-bootstrap/ng-bootstrap'; +import {CommonModule} from '@angular/common'; +import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from '@angular/forms'; +import {NlmTypeaheadComponent} from '../nlm-typeahead/nlm-typeahead.component'; +import {HighlightModule} from 'ngx-highlightjs'; +import {FhirDatatableModule} from '../fhir-datatable/fhir-datatable.module'; +import {ResourceType} from '../../../lib/models/constants'; +import {ResourceFhir} from '../../models/fasten/resource_fhir'; +import {FastenApiService} from '../../services/fasten-api.service'; +import {ResponseWrapper} from '../../models/response-wrapper'; +import {fhirModelFactory} from '../../../lib/models/factory'; +import {OrganizationModel} from '../../../lib/models/resources/organization-model'; +import {AddressModel} from '../../../lib/models/datatypes/address-model'; +import {CodableConceptModel} from '../../../lib/models/datatypes/codable-concept-model'; +import {uuidV4} from '../../../lib/utils/uuid'; +import {PractitionerModel} from '../../../lib/models/resources/practitioner-model'; +import {parseFullName} from 'parse-full-name' + +@Component({ + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + NlmTypeaheadComponent, + HighlightModule, + NgbTooltipModule, + NgbNavModule, + FhirDatatableModule + ], + selector: 'app-medical-record-wizard-add-practitioner', + templateUrl: './medical-record-wizard-add-practitioner.component.html', + styleUrls: ['./medical-record-wizard-add-practitioner.component.scss'] +}) +export class MedicalRecordWizardAddPractitionerComponent implements OnInit { + @Input() debugMode: boolean = false; + @Input() disabledResourceIds: string[] = []; + + activeId: string = 'find' + + newPractitionerTypeaheadForm: FormGroup + newPractitionerForm: FormGroup //ResourceCreatePractitioner + + //find tab options + selectedPractitioner: {source_resource_id: string,source_resource_type: ResourceType, resource: ResourceFhir} = null + totalPractitioners: number = 0 + + constructor( + public activeModal: NgbActiveModal, + private fastenApi: FastenApiService, + ) { } + + ngOnInit(): void { + this.resetPractitionerForm() + + //get a count of all the known practitioners + this.fastenApi.queryResources({ + "select": [], + "from": "Practitioner", + "where": {}, + "aggregations": { + "count_by": {"field": "*"} + } + }).subscribe((resp: ResponseWrapper) => { + this.totalPractitioners = resp.data?.[0].value + }) + } + changeTab(id: string) { + if(this.activeId != id){ + this.activeId = id + this.resetPractitionerForm() + this.selectedPractitioner = null + } + } + selectionChanged(event) { + console.log("SELECTION CHANGED", event) + this.selectedPractitioner = event + } + get submitEnabled() { + return (this.activeId == 'create' && this.newPractitionerForm.valid) || + (this.activeId == 'find' && this.selectedPractitioner != null) + } + + submit() { + if(this.activeId == 'create'){ + this.newPractitionerForm.markAllAsTouched() + if(this.newPractitionerForm.valid){ + this.activeModal.close({ + action: this.activeId, + data: this.practitionerFormToDisplayModel(this.newPractitionerForm) + }); + } + } else if(this.activeId == 'find'){ + if(this.selectedPractitioner != null){ + this.activeModal.close({ + action: this.activeId, + data: fhirModelFactory(this.selectedPractitioner.source_resource_type, this.selectedPractitioner.resource) + }); + } + } + } + + private practitionerFormToDisplayModel(form: FormGroup): PractitionerModel { + let address = new AddressModel(null) + address.city = form.get('address').get('city').value + address.line = [ + form.get('address').get('line1').value, + form.get('address').get('line2').value, + ] + address.state = form.get('address').get('state').value + address.country = form.get('address').get('country').value + address.postalCode = form.get('address').get('zip').value + + let model = new PractitionerModel({}) + model.source_resource_id = form.get('id').value + model.identifier = form.get('identifier').value + model.name = [] + model.address = [address] + model.telecom = [] + model.qualification = [] + if (form.get('phone').value) { + model.telecom.push({ + system: 'phone', + value: form.get('phone').value, + use: 'work' + }) + } + if(form.get('fax').value) { + model.telecom.push({ + system: 'fax', + value: form.get('fax').value, + use: 'work' + }) + } + if(form.get('email').value) { + model.telecom.push({ + system: 'email', + value: form.get('email').value, + use: 'work' + }) + } + if(form.get('profession').value) { + model.qualification = form.get('profession').value.identifier + } + if(form.get('name').value) { + let nameParts = parseFullName(form.get('name').value) + model.name.push({ + givenName: nameParts.first, + familyName: nameParts.last, + suffix: nameParts.suffix, + textName: form.get('name').value, + use: 'official', + displayName: form.get('name').value, + }) + } + + if(!model.source_resource_id){ + console.warn("No source_resource_id set for Organization, generating one") + model.source_resource_id = uuidV4(); + } + + return model + } + + private resetPractitionerForm(){ + this.newPractitionerTypeaheadForm = new FormGroup({ + data: new FormControl(null, Validators.required), + }) + this.newPractitionerTypeaheadForm.valueChanges.subscribe(form => { + console.log("CHANGE INDIVIDUAL IN MODAL", form) + let val = form.data + if(val == null){ + //reset the dependant fields (user cleared the text box) + this.newPractitionerForm.get('id').setValue(null) + this.newPractitionerForm.get('profession').setValue(null) + this.newPractitionerForm.get('identifier').setValue(null); + this.newPractitionerForm.get('phone').setValue(null); + this.newPractitionerForm.get('fax').setValue(null); + let addressGroup = this.newPractitionerForm.get('address') + addressGroup.get('line1').setValue(null) + addressGroup.get('line2').setValue(null) + addressGroup.get('city').setValue(null) + addressGroup.get('state').setValue(null) + addressGroup.get('zip').setValue(null) + addressGroup.get('country').setValue(null) + this.newPractitionerForm.get('name').setValue(null); + return + } + + + if(val.id){ + this.newPractitionerForm.get('id').setValue(val.id) + } + if(val.provider_type){ + this.newPractitionerForm.get('profession').setValue(val.provider_type) + } + if(val.identifier){ + this.newPractitionerForm.get('identifier').setValue( val.identifier); + } + if(form.data.provider_phone){ + this.newPractitionerForm.get('phone').setValue( val.provider_phone); + } + if(val.provider_fax){ + this.newPractitionerForm.get('fax').setValue(val.provider_fax); + } + + if(val.provider_address){ + let addressGroup = this.newPractitionerForm.get('address') + addressGroup.get('line1').setValue(val.provider_address.line1) + addressGroup.get('line2').setValue(val.provider_address.line2) + addressGroup.get('city').setValue(val.provider_address.city) + addressGroup.get('state').setValue(val.provider_address.state) + addressGroup.get('zip').setValue(val.provider_address.zip) + addressGroup.get('country').setValue(val.provider_address.country) + } + if(val.text) { + this.newPractitionerForm.get('name').setValue( val.text); + } + }); + + + this.newPractitionerForm = new FormGroup({ + id: new FormControl(null), + identifier: new FormControl([]), + name: new FormControl(null, Validators.required), + profession: new FormControl(null, Validators.required), + phone: new FormControl(null, Validators.pattern('[- +()0-9]+')), + fax: new FormControl(null, Validators.pattern('[- +()0-9]+')), + email: new FormControl(null, Validators.email), + address: new FormGroup({ + line1: new FormControl(null), + line2: new FormControl(null), + city: new FormControl(null), + state: new FormControl(null), + zip: new FormControl(null), + country: new FormControl(null), + }) + }) + } +} diff --git a/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.stories.ts b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.stories.ts new file mode 100644 index 00000000..f65e7f5d --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.stories.ts @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {MedicalRecordWizardAddPractitionerComponent} from './medical-record-wizard-add-practitioner.component'; +import {applicationConfig} from '@storybook/angular'; +import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {importProvidersFrom} from '@angular/core'; + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Components/MedicalRecordWizardAddPractitioner', + component: MedicalRecordWizardAddPractitionerComponent, + decorators: [ + applicationConfig({ + providers: [ + importProvidersFrom(HttpClientTestingModule), + NgbActiveModal, + { + provide: HttpClient, + useClass: HttpClient + }, + { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + } + ] + }), + ], + tags: ['autodocs'], + render: (args: MedicalRecordWizardAddPractitionerComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +export const Primary: Story = {}; + diff --git a/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.html b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.html new file mode 100644 index 00000000..2b3c37e0 --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.html @@ -0,0 +1,428 @@ +
+ + + +
diff --git a/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.scss b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.spec.ts b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.spec.ts new file mode 100644 index 00000000..0fa570ba --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MedicalRecordWizardComponent } from './medical-record-wizard.component'; +import {NgbActiveModal, NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; + +describe('MedicalRecordWizardComponent', () => { + let component: MedicalRecordWizardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ MedicalRecordWizardComponent, HttpClientTestingModule ], + providers: [ NgbActiveModal, NgbModal, { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + } ], + }) + .compileComponents(); + + fixture = TestBed.createComponent(MedicalRecordWizardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.ts b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.ts new file mode 100644 index 00000000..934f8918 --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.component.ts @@ -0,0 +1,398 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {HighlightModule} from 'ngx-highlightjs'; +import { + NgbActiveModal, + NgbDatepickerModule, + NgbModal, + NgbNavModule, + NgbTooltipModule, + NgbTypeaheadModule, +} from '@ng-bootstrap/ng-bootstrap'; +import {FastenApiService} from '../../services/fasten-api.service'; +import {AbstractControl, FormArray, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from '@angular/forms'; +import {NlmSearchResults} from '../../services/nlm-clinical-table-search.service'; +import {NlmTypeaheadComponent} from '../nlm-typeahead/nlm-typeahead.component'; +import {CommonModule} from '@angular/common'; +import {NgSelectModule} from '@ng-select/ng-select'; +import {ResourceCreateAttachment, ResourceCreateOrganization, ResourceCreatePractitioner} from '../../models/fasten/resource_create'; +import {uuidV4} from '../../../lib/utils/uuid'; +import { + MedicalRecordWizardAddPractitionerComponent +} from '../medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component'; +import { + MedicalRecordWizardAddOrganizationComponent +} from '../medical-record-wizard-add-organization/medical-record-wizard-add-organization.component'; +import { + MedicalRecordWizardAddAttachmentComponent +} from '../medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component'; +import {GenerateR4ResourceLookup, WizardFhirResourceWrapper} from './medical-record-wizard.utilities'; +import {EncounterModel} from '../../../lib/models/resources/encounter-model'; +import {SharedModule} from '../shared.module'; +import {FhirCardModule} from '../fhir-card/fhir-card.module'; +import {OrganizationModel} from '../../../lib/models/resources/organization-model'; +import {PractitionerModel} from '../../../lib/models/resources/practitioner-model'; +import {PipesModule} from '../../pipes/pipes.module'; +import { + MedicalRecordWizardAddEncounterComponent +} from '../medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component'; +import {generateReferenceUriFromResourceOrReference, internalResourceReferenceUri} from '../../../lib/utils/bundle_references'; + +import { + FhirResource, + List, Reference +} from 'fhir/r4'; + +@Component({ + standalone: true, + imports: [ + NgbNavModule, + CommonModule, + ReactiveFormsModule, + FormsModule, + NgbTypeaheadModule, + NgbDatepickerModule, + NgbTooltipModule, + NlmTypeaheadComponent, + NgSelectModule, + HighlightModule, + FhirCardModule, + PipesModule + ], + selector: 'app-medical-record-wizard', + templateUrl: './medical-record-wizard.component.html', + styleUrls: ['./medical-record-wizard.component.scss'] +}) +export class MedicalRecordWizardComponent implements OnInit { + @Input() existingEncounter: EncounterModel; + @Input() debugMode = false; + @Input() form!: FormGroup; + + active = 'encounter'; + submitWizardLoading = false; + + + constructor( + public activeModal: NgbActiveModal, + private modalService: NgbModal, + private fastenApi: FastenApiService, + ) { + + + } + + ngOnInit(): void { + this.form = new FormGroup({ + encounter: new FormGroup({ + data: new FormControl({}, Validators.required), + action: new FormControl(null), + }, Validators.required), + + medications: new FormArray([]), + procedures: new FormArray([]), + practitioners: new FormArray([]), + organizations: new FormArray([]), + attachments: new FormArray([]), + }); + + if(this.existingEncounter){ + this.addEncounter({data: this.existingEncounter, action: 'find'}); + } + } + + // + get medications(): FormArray { + return this.form.controls["medications"] as FormArray; + } + get procedures(): FormArray { + return this.form.controls["procedures"] as FormArray; + } + get practitioners(): FormArray { + return this.form.controls["practitioners"] as FormArray; + } + get organizations(): FormArray { + return this.form.controls["organizations"] as FormArray; + } + get attachments(): FormArray { + return this.form.controls["attachments"] as FormArray; + } + // + + // + deleteMedication(index: number) { + this.medications.removeAt(index); + } + deleteProcedure(index: number) { + this.procedures.removeAt(index); + } + deletePractitioner(index: number) { + this.practitioners.removeAt(index); + } + deleteOrganization(index: number) { + this.organizations.removeAt(index); + } + deleteAttachment(index: number) { + this.attachments.removeAt(index); + } + // + + // + addEncounter(openEncounterResult: WizardFhirResourceWrapper){ + let encounter = openEncounterResult.data; + this.existingEncounter = encounter; + + let clonedEncounter = this.deepClone(encounter) as EncounterModel; + clonedEncounter.related_resources = {}; + + this.form.get("encounter").get('data').setValue(clonedEncounter); + this.form.get("encounter").get('action').setValue(openEncounterResult.action); + } + + addMedication(){ + const medicationGroup = new FormGroup({ + data: new FormControl(null, Validators.required), + status: new FormControl(null, Validators.required), + dosage: new FormControl({ + value: '', disabled: true + }), + started: new FormControl(null, Validators.required), + stopped: new FormControl(null), + whystopped: new FormControl(null), + requester: new FormControl(null, Validators.required), + instructions: new FormControl(null), + attachments: new FormControl([]), + }); + + medicationGroup.get("data").valueChanges.subscribe(val => { + medicationGroup.get("dosage").enable(); + //TODO: find a way to create dependant dosage information based on medication data. + }); + + this.medications.push(medicationGroup); + } + addProcedure(){ + const procedureGroup = new FormGroup({ + data: new FormControl(null, Validators.required), + whendone: new FormControl(null, Validators.required), + performer: new FormControl(null), + location: new FormControl(null), + comment: new FormControl(''), + attachments: new FormControl([]), + }); + + this.procedures.push(procedureGroup); + } + addPractitioner(openPractitionerResult: WizardFhirResourceWrapper){ + const practitionerGroup = new FormGroup({ + data: new FormControl(openPractitionerResult.data), + action: new FormControl(openPractitionerResult.action), + + }); + this.practitioners.push(practitionerGroup); + } + addOrganization(openOrganizationResult: WizardFhirResourceWrapper) { + const organizationGroup = new FormGroup({ + data: new FormControl(openOrganizationResult.data), + action: new FormControl(openOrganizationResult.action), + }); + this.organizations.push(organizationGroup); + } + addAttachment(attachment: ResourceCreateAttachment){ + const attachmentGroup = new FormGroup({ + id: new FormControl(attachment.id, Validators.required), + name: new FormControl(attachment.name, Validators.required), + category: new FormControl(attachment.category, Validators.required), + file_type: new FormControl(attachment.file_type, Validators.required), + file_name: new FormControl(attachment.file_name, Validators.required), + file_content: new FormControl(attachment.file_content, Validators.required), + file_size: new FormControl(attachment.file_size), + }); + + this.attachments.push(attachmentGroup); + } + // + + // + openEncounterModal() { + let modalRef = this.modalService.open(MedicalRecordWizardAddEncounterComponent, { + ariaLabelledBy: 'modal-encounter', + size: 'lg', + }) + modalRef.componentInstance.debugMode = this.debugMode; + modalRef.result.then( + (result) => { + console.log('Closing, saving form', result); + // add this to the list of organization + //TODO + this.addEncounter(result); + }, + (err) => { + console.log('Closed without saving', err); + }, + ); + + } + + openPractitionerModal(formGroup?: AbstractControl, controlName?: string) { + let disabledResourceIds = []; + disabledResourceIds.push(...(this.practitioners?.value || []).map(practitioner => practitioner.data.source_resource_id)); + disabledResourceIds.push(...(this.existingEncounter?.related_resources?.['Practitioner'] || []).map(practitioner => practitioner.source_resource_id)); + + // this.resetPractitionerForm() + let modalRef = this.modalService.open(MedicalRecordWizardAddPractitionerComponent, { + ariaLabelledBy: 'modal-practitioner', + size: 'lg', + }) + modalRef.componentInstance.debugMode = this.debugMode; + modalRef.componentInstance.disabledResourceIds = disabledResourceIds; + modalRef.result.then( + (result) => { + console.log('Closing, saving form', result); + // add this to the list of organization + this.addPractitioner(result); + if(formGroup && controlName){ + //set this practitioner to the current select box + formGroup.get(controlName).setValue(generateReferenceUriFromResourceOrReference(result.data)); + } + }, + (err) => { + console.log('Closed without saving', err); + }, + ); + + } + + openOrganizationModal(formGroup?: AbstractControl, controlName?: string) { + let disabledResourceIds = []; + disabledResourceIds.push(...(this.organizations?.value || []).map(org => org.data.source_resource_id)); + disabledResourceIds.push(...(this.existingEncounter?.related_resources?.['Organization'] || []).map(org => org.source_resource_id)); + + let modalRef = this.modalService.open(MedicalRecordWizardAddOrganizationComponent, { + ariaLabelledBy: 'modal-organization', + size: 'lg', + }) + modalRef.componentInstance.debugMode = this.debugMode; + modalRef.componentInstance.disabledResourceIds = disabledResourceIds; + modalRef.result.then( + (result) => { + console.log('Closing, saving form', result); + //add this to the list of organization + this.addOrganization(result); + if(formGroup && controlName){ + //set this practitioner to the current select box + formGroup.get(controlName).setValue(generateReferenceUriFromResourceOrReference(result.data)); + } + }, + (err) => { + console.log('Closed without saving', err); + }, + ); + } + + openAttachmentModal(formGroup?: AbstractControl, controlName?: string) { + let modalRef = this.modalService.open(MedicalRecordWizardAddAttachmentComponent, { + ariaLabelledBy: 'modal-attachment', + size: 'lg' + }) + modalRef.componentInstance.debugMode = this.debugMode; + modalRef.result.then( + (result) => { + console.log('Closing, saving form', result); + //add this to the list of organization + result.id = uuidV4(); + this.addAttachment(result); + + if(formGroup && controlName){ + + //add this attachment id to the current FormArray + let controlArrayVal = formGroup.get(controlName).getRawValue(); + controlArrayVal.push(result.id) + formGroup.get(controlName).setValue(controlArrayVal); + } + }, + (err) => { + console.log('Closed without saving', err); + }, + ); + } + + // + + + onSubmit() { + console.log(this.form.getRawValue()) + this.form.markAllAsTouched() + if (this.form.valid) { + console.log('form submitted'); + this.submitWizardLoading = true; + + let resourceStorage = GenerateR4ResourceLookup(this.form.getRawValue()); + + //generate a ndjson file from the resourceList + //make sure we extract the encounter resource + + let fhirListResource = { + resourceType: 'List', + entry: [], + encounter: null, + contained: [] + } as List + + let encounter = null + for(let resourceType in resourceStorage) { + if (resourceType === 'Encounter') { + //set the encounter to the first encounter + let [encounterId] = Object.keys(resourceStorage[resourceType]) + encounter = resourceStorage[resourceType][encounterId] + + if(!(encounter.type && encounter.reference)){ + //this is not a reference + fhirListResource.contained.push(encounter) + } + continue + } + + for(let resourceId in resourceStorage[resourceType]) { + let resourceFromStorage = resourceStorage[resourceType][resourceId] + if((resourceFromStorage as Reference).type && (resourceFromStorage as Reference).reference){ + //this is a reference + fhirListResource.entry.push({ + item: { + reference: generateReferenceUriFromResourceOrReference(resourceFromStorage) + } + }) + } else { + //this is not a reference + fhirListResource.contained.push(resourceFromStorage as FhirResource) + } + } + } + + //set the encounter reference + fhirListResource.encounter = { + reference: generateReferenceUriFromResourceOrReference(encounter), + } + + this.fastenApi.createRelatedResourcesFastenSource(fhirListResource).subscribe( + (resp) => { + console.log(resp) + this.submitWizardLoading = false; + this.activeModal.close() + }, + (err) => { + console.log(err) + this.submitWizardLoading = false; + } + ) + + } + } + + + // + private deepClone(obj: any):any { + if(!obj) return obj; + return JSON.parse(JSON.stringify(obj)); + } + // + +} diff --git a/frontend/src/app/components/medical-record-wizard/medical-record-wizard.stories.ts b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.stories.ts new file mode 100644 index 00000000..085a4a4a --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.stories.ts @@ -0,0 +1,46 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {MedicalRecordWizardComponent} from './medical-record-wizard.component'; +import {applicationConfig} from '@storybook/angular'; +import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {HttpClient} from '@angular/common/http'; +import {HttpClientTestingModule} from '@angular/common/http/testing'; +import {importProvidersFrom} from '@angular/core'; + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Components/MedicalRecordWizard', + component: MedicalRecordWizardComponent, + decorators: [ + applicationConfig({ + providers: [ + importProvidersFrom(HttpClientTestingModule), + NgbActiveModal, + { + provide: HttpClient, + useClass: HttpClient + }, + { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + } + ] + }), + ], + tags: ['autodocs'], + render: (args: MedicalRecordWizardComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +export const Primary: Story = {}; + diff --git a/frontend/src/app/components/medical-record-wizard/medical-record-wizard.utilities.ts b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.utilities.ts new file mode 100644 index 00000000..aa8a67ca --- /dev/null +++ b/frontend/src/app/components/medical-record-wizard/medical-record-wizard.utilities.ts @@ -0,0 +1,443 @@ +import { + MedicalRecordWizardFormCreate, + ResourceCreateCondition, ResourceCreateAttachment, ResourceCreateMedication, + ResourceCreateOrganization, ResourceCreatePractitioner, + ResourceCreateProcedure +} from '../../models/fasten/resource_create'; +import { + Condition, + Medication, + Procedure, + Location as FhirLocation, + BundleEntry, + Resource, + Bundle, + Organization, + Practitioner, MedicationRequest, Patient, Encounter, DocumentReference, Media, DiagnosticReport, Reference, Binary, HumanName +} from 'fhir/r4'; +import {uuidV4} from '../../../lib/utils/uuid'; +import {OrganizationModel} from '../../../lib/models/resources/organization-model'; +import {PractitionerModel} from '../../../lib/models/resources/practitioner-model'; +import {EncounterModel} from '../../../lib/models/resources/encounter-model'; +import {ReferenceModel} from '../../../lib/models/datatypes/reference-model'; +import {FastenDisplayModel} from '../../../lib/models/fasten/fasten-display-model'; +import {generateReferenceUriFromResourceOrReference} from '../../../lib/utils/bundle_references'; +import {HumanNameModel} from '../../../lib/models/datatypes/human-name-model'; + +export interface WizardFhirResourceWrapper { + data: T, + action: 'find'|'create' +} + +interface ResourceStorage { + [resourceType: string]: { + [resourceId: string]: Condition | Patient | MedicationRequest | Organization | FhirLocation | Practitioner | Procedure | Encounter | DocumentReference | Media | DiagnosticReport | Binary | Reference + } +} + +export function GenerateR4ResourceLookup(resourceCreate: MedicalRecordWizardFormCreate): ResourceStorage { + let resourceStorage: ResourceStorage = {} + // resourceStorage = placeholderR4Patient(resourceStorage) + // resourceStorage = resourceCreateConditionToR4Condition(resourceStorage, resourceCreate.condition) + + resourceStorage = resourceCreateEncounterToR4Encounter(resourceStorage, resourceCreate.encounter) + + for(let attachment of resourceCreate.attachments) { + if(attachment.file_type == 'application/dicom' || + attachment.category.id == '18726-0' || //Radiology studies (set) + attachment.category.id == '27897-8' || // Neuromuscular electrophysiology studies (set) + attachment.category.id == '18748-4' // Diagnostic imaging study + ) { + //Diagnostic imaging study (DiagnosticReport -> Media) + resourceStorage = resourceAttachmentToR4DiagnosticReport(resourceStorage, attachment) + } + else { + resourceStorage = resourceAttachmentToR4DocumentReference(resourceStorage, attachment) + } + } + + for(let organization of resourceCreate.organizations) { + resourceStorage = resourceCreateOrganizationToR4Organization(resourceStorage, organization) + } + for(let practitioner of resourceCreate.practitioners) { + resourceStorage = resourceCreatePractitionerToR4Practitioner(resourceStorage, practitioner) + } + for(let medication of resourceCreate.medications) { + resourceStorage = resourceCreateMedicationToR4MedicationRequest(resourceStorage, medication) + } + for(let procedure of resourceCreate.procedures) { + resourceStorage = resourceCreateProcedureToR4Procedure(resourceStorage, procedure) + } + + + //DocumentReference -> (Optional) Binary + //DiagnosticReport -> Media + //ImagingStudy + //ImagingSelection + + + + return resourceStorage +} + +//Private methods + +// this model is based on FHIR401 Resource Encounter - http://hl7.org/fhir/R4/encounter.html +function resourceCreateEncounterToR4Encounter(resourceStorage: ResourceStorage, resourceEncounter: WizardFhirResourceWrapper): ResourceStorage { + resourceStorage['Encounter'] = resourceStorage['Encounter'] || {} + console.warn("resourceEncounter", resourceEncounter) + + if (resourceEncounter.action == 'create') { + let createdResourceEncounter = { + resourceType: 'Encounter', + id: resourceEncounter.data.source_resource_id, + serviceType: resourceEncounter.data.code, + status: "finished", + // participant: [ + // { + // individual: { + // reference: `urn:uuid:${resourceCreateProcedure.performer}` //Practitioner + // } + // } + // ], + participant: [], + period: { + start: resourceEncounter.data.period_start, + end: resourceEncounter.data.period_end, + }, + reasonReference: [], + serviceProvider: { + // reference: `urn:uuid:${resourceCreateProcedure.location}` //Organization + } + } as Encounter + resourceStorage['Encounter'][createdResourceEncounter.id] = createdResourceEncounter + } else { + let foundResourceEncounter = { + type: 'Encounter', + reference: generateReferenceUriFromResourceOrReference(resourceEncounter.data), + } + resourceStorage['Encounter'][foundResourceEncounter.reference] = foundResourceEncounter + } + + return resourceStorage +} + + + +// this model is based on FHIR401 Resource Condition - http://hl7.org/fhir/R4/condition.html +// function resourceCreateConditionToR4Condition(resourceStorage: ResourceStorage, resourceCreateCondition: ResourceCreateCondition): ResourceStorage { +// resourceStorage['Condition'] = resourceStorage['Condition'] || {} +// resourceStorage['Encounter'] = resourceStorage['Encounter'] || {} +// +// let note = [] +// if (resourceCreateCondition.description) { +// note.push({ +// text: resourceCreateCondition.description, +// }) +// } +// +// let conditionResource = { +// subject: { +// reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient +// }, +// resourceType: 'Condition', +// id: uuidV4(), +// code: { +// coding: resourceCreateCondition.data.identifier || [], +// text: resourceCreateCondition.data.identifier[0].display, +// }, +// clinicalStatus: { +// "coding": [ +// { +// "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", +// "code": resourceCreateCondition.status, +// } +// ] +// }, +// onsetDateTime: `${new Date(resourceCreateCondition.started.year, resourceCreateCondition.started.month-1,resourceCreateCondition.started.day).toISOString()}`, +// abatementDateTime: resourceCreateCondition.stopped ? `${new Date(resourceCreateCondition.stopped.year,resourceCreateCondition.stopped.month-1, resourceCreateCondition.stopped.day).toISOString()}` : null, +// recordedDate: new Date().toISOString(), +// note: note +// } as Condition +// +// +// +// resourceStorage['Condition'][conditionResource.id] = conditionResource +// return resourceStorage +// } + +// this model is based on FHIR401 Resource Procedure - http://hl7.org/fhir/R4/procedure.html +function resourceCreateProcedureToR4Procedure(resourceStorage: ResourceStorage, resourceCreateProcedure: ResourceCreateProcedure): ResourceStorage { + resourceStorage['Procedure'] = resourceStorage['Procedure'] || {} + + let note = [] + if (resourceCreateProcedure.comment) { + note.push({ + text: resourceCreateProcedure.comment, + }) + } + + let encounterResource = findEncounter(resourceStorage) as Encounter | Reference + + let procedureResource = { + status: "completed", + resourceType: 'Procedure', + id: uuidV4(), + code: { + coding: resourceCreateProcedure.data.identifier || [], + text: resourceCreateProcedure.data.identifier?.[0]?.display, + }, + performedDateTime: `${new Date(resourceCreateProcedure.whendone.year, resourceCreateProcedure.whendone.month-1,resourceCreateProcedure.whendone.day).toISOString()}`, + encounter: { + reference: generateReferenceUriFromResourceOrReference(encounterResource) //Encounter + }, + report: (resourceCreateProcedure.attachments || []).map(attachmentId => { + return { + reference: `urn:uuid:${attachmentId}` //DocumentReference or DiagnosticReport + } + }), + performer: [ + { + actor: { + reference: resourceCreateProcedure.performer //Practitioner + }, + onBehalfOf: { + reference: resourceCreateProcedure.location //Organization + } + } + ], + note: note, + } as Procedure + resourceStorage['Procedure'][procedureResource.id] = procedureResource + + return resourceStorage +} + +// this model is based on FHIR401 Resource Organization - http://hl7.org/fhir/R4/organization.html +function resourceCreateOrganizationToR4Organization(resourceStorage: ResourceStorage, resourceOrganization: WizardFhirResourceWrapper): ResourceStorage { + resourceStorage['Organization'] = resourceStorage['Organization'] || {} + if (resourceOrganization.action == 'create') { + let organizationResource = { + resourceType: 'Organization', + id: resourceOrganization.data.source_resource_id, + name: resourceOrganization.data.name, + identifier: resourceOrganization.data.identifier || [], + type: resourceOrganization.data.type || [], + address: resourceOrganization.data.addresses || [], + telecom: resourceOrganization.data.telecom, + active: true, + } as Organization + + resourceStorage['Organization'][organizationResource.id] = organizationResource + } else { + let foundResourceOrganization = { + type: 'Organization', + reference: generateReferenceUriFromResourceOrReference(resourceOrganization.data), + } + resourceStorage['Organization'][foundResourceOrganization.reference] = foundResourceOrganization + } + + return resourceStorage + +} + +// this model is based on FHIR401 Resource Practitioner - http://hl7.org/fhir/R4/practitioner.html +function resourceCreatePractitionerToR4Practitioner(resourceStorage: ResourceStorage, resourcePractitioner: WizardFhirResourceWrapper): ResourceStorage { + resourceStorage['Practitioner'] = resourceStorage['Practitioner'] || {} + if (resourcePractitioner.action == 'create') { + + let humanName = [] as HumanName[] + if (resourcePractitioner.data.name) { + humanName = resourcePractitioner.data.name.map((name: HumanNameModel) => { + return { + family: name.familyName, + given: name.givenName.split(', '), + suffix: name.suffix.split(', '), + text: name.displayName, + use: 'official', + } + }) + } + + let practitionerResource = { + resourceType: 'Practitioner', + id: resourcePractitioner.data.source_resource_id, + name: humanName, + identifier: resourcePractitioner.data.identifier || [], + address: resourcePractitioner.data.address || [], + telecom: resourcePractitioner.data.telecom || [], + active: true, + } as Practitioner + + if(resourcePractitioner.data.qualification){ + practitionerResource.qualification = [{ + code: { + coding: resourcePractitioner.data.qualification || [] + }, + }] + } + + resourceStorage['Practitioner'][practitionerResource.id] = practitionerResource + } else { + let foundResourcePractitioner = { + type: 'Practitioner', + reference: generateReferenceUriFromResourceOrReference(resourcePractitioner.data), + } + resourceStorage['Practitioner'][foundResourcePractitioner.reference] = foundResourcePractitioner + } + return resourceStorage +} + +// this model is based on FHIR401 Resource Medication - https://www.hl7.org/fhir/R4/MedicationRequest.html +function resourceCreateMedicationToR4MedicationRequest(resourceStorage: ResourceStorage, resourceCreateMedication: ResourceCreateMedication): ResourceStorage { + resourceStorage['MedicationRequest'] = resourceStorage['MedicationRequest'] || {} + + let encounterResource = findEncounter(resourceStorage) as Encounter | Reference + + let medicationRequestResource = { + id: uuidV4(), + resourceType: 'MedicationRequest', + status: resourceCreateMedication.status, + statusReason: { + coding: resourceCreateMedication.whystopped?.identifier || [], + }, + intent: 'order', + medicationCodeableConcept: { + coding: resourceCreateMedication.data.identifier || [], + }, + encounter: { + reference: generateReferenceUriFromResourceOrReference(encounterResource) //Encounter + }, + authoredOn: `${new Date(resourceCreateMedication.started.year,resourceCreateMedication.started.month-1,resourceCreateMedication.started.day).toISOString()}`, + requester: { + reference: resourceCreateMedication.requester // Practitioner + }, + supportingInformation: (resourceCreateMedication.attachments || []).map((attachmentId) => { + return { + reference: `urn:uuid:${attachmentId}` //DocumentReference or DiagnosticReport + } + }), + note: [ + { + text: resourceCreateMedication.instructions, + } + ], + dispenseRequest: { + validityPeriod: { + start: `${new Date(resourceCreateMedication.started.year,resourceCreateMedication.started.month-1,resourceCreateMedication.started.day).toISOString()}`, + end: resourceCreateMedication.stopped ? `${new Date(resourceCreateMedication.stopped.year,resourceCreateMedication.stopped.month-1,resourceCreateMedication.stopped.day).toISOString()}` : null, + }, + }, + } as MedicationRequest + resourceStorage['MedicationRequest'][medicationRequestResource.id] = medicationRequestResource + + return resourceStorage +} + +function resourceAttachmentToR4DocumentReference(resourceStorage: ResourceStorage, resourceAttachment: ResourceCreateAttachment): ResourceStorage { + let encounterResource = findEncounter(resourceStorage) as Encounter | Reference + + resourceStorage['Binary'] = resourceStorage['Binary'] || {} + let binaryResource = { + id: uuidV4(), + resourceType: 'Binary', + contentType: resourceAttachment.file_type, + data: resourceAttachment.file_content, + } as Binary + resourceStorage['Binary'][binaryResource.id] = binaryResource + + resourceStorage['DocumentReference'] = resourceStorage['DocumentReference'] || {} + + let documentReferenceResource = { + id: resourceAttachment.id, + resourceType: 'DocumentReference', + status: 'current', + category: [ + { + coding: resourceAttachment.category.identifier || [], + text: resourceAttachment.category.text, + } + ], + // description: resourceAttachment.description, + content: [ + { + attachment: { + contentType: resourceAttachment.file_type, + url: `urn:uuid:${binaryResource.id}`, //Binary + title: resourceAttachment.name, + } + } + ], + context: [ + { + encounter: { + reference: generateReferenceUriFromResourceOrReference(encounterResource) //Encounter + }, + } + ] + // date: `${new Date(resourceDocumentReference.date.year,resourceDocumentReference.date.month-1,resourceDocumentReference.date.day).toISOString()}`, + } as DocumentReference + resourceStorage['DocumentReference'][documentReferenceResource.id] = documentReferenceResource + + return resourceStorage +} + +function resourceAttachmentToR4DiagnosticReport(resourceStorage: ResourceStorage, resourceAttachment: ResourceCreateAttachment): ResourceStorage { + let encounterResource = findEncounter(resourceStorage) as Encounter | Reference + + resourceStorage['Binary'] = resourceStorage['Binary'] || {} + let binaryResource = { + id: uuidV4(), + resourceType: 'Binary', + contentType: resourceAttachment.file_type, + data: resourceAttachment.file_content, + } as Binary + resourceStorage['Binary'][binaryResource.id] = binaryResource + + resourceStorage['Media'] = resourceStorage['Media'] || {} + + let mediaResource = { + id: uuidV4(), + resourceType: 'Media', + status: 'completed', + type: { + coding: resourceAttachment.category.identifier || [], + display: resourceAttachment.category.text, + }, + content: { + contentType: resourceAttachment.file_type, + url: `urn:uuid:${binaryResource.id}`, //Binary, + title: resourceAttachment.name, + }, + } as Media + resourceStorage['Media'][mediaResource.id] = mediaResource + + resourceStorage['DiagnosticReport'] = resourceStorage['DiagnosticReport'] || {} + let diagnosticReportResource = { + id: resourceAttachment.id, + resourceType: 'DiagnosticReport', + status: 'final', + code: { + coding: resourceAttachment.category.identifier || [], + }, + encounter: { + reference: generateReferenceUriFromResourceOrReference(encounterResource) //Encounter + }, + media: [ + { + link: { + reference: `urn:uuid:${mediaResource.id}` //Media + } + }, + ], + } as DiagnosticReport + resourceStorage['DiagnosticReport'][diagnosticReportResource.id] = diagnosticReportResource + + return resourceStorage +} + + +function findEncounter(resourceStorage: ResourceStorage): Encounter | Reference { + let [encounterId] = Object.keys(resourceStorage['Encounter']) + return resourceStorage['Encounter'][encounterId] as Encounter | Reference +} diff --git a/frontend/src/app/components/medical-sources-card/medical-sources-card.component.ts b/frontend/src/app/components/medical-sources-card/medical-sources-card.component.ts index aaf15792..5e1fdbd5 100644 --- a/frontend/src/app/components/medical-sources-card/medical-sources-card.component.ts +++ b/frontend/src/app/components/medical-sources-card/medical-sources-card.component.ts @@ -30,6 +30,8 @@ export class MedicalSourcesCardComponent implements OnInit { } if(sourceItem.source?.source_type == 'manual') { return 'Uploaded ' + moment(sourceItem.source?.created_at).format('MMM DD, YYYY') + } else if(sourceItem.source?.source_type == 'fasten'){ + return 'Fasten Health' } return "Unknown" } diff --git a/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.html b/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.html index 27bf1c74..21b09a6b 100644 --- a/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.html +++ b/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.html @@ -18,7 +18,7 @@
- - + @@ -80,14 +80,14 @@ {{medication.display }} - +
  • {{medication.title }} - +
  • @@ -101,7 +101,7 @@ {{procedure.display}} - + @@ -115,7 +115,7 @@ {{diagnosticReport.sort_title || diagnosticReport.title}} - + @@ -128,7 +128,7 @@ {{documentReference.sort_title || documentReference.title}} - + diff --git a/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.ts b/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.ts index 62c6b579..20ce53ff 100644 --- a/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.ts +++ b/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.ts @@ -15,12 +15,14 @@ import {ConditionModel} from '../../../lib/models/resources/condition-model'; import {RecResourceRelatedDisplayModel} from '../../../lib/utils/resource_related_display_model'; import {CodingModel} from '../../../lib/models/datatypes/coding-model'; +/** + * @deprecated This condition panel is no longer in use, the timeline panel allows users to view resources by encounter + */ @Component({ selector: 'app-report-medical-history-condition', templateUrl: './report-medical-history-condition.component.html', styleUrls: ['./report-medical-history-condition.component.scss'] }) - export class ReportMedicalHistoryConditionComponent implements OnInit { diff --git a/frontend/src/app/components/report-medical-history-editor/report-medical-history-editor.component.ts b/frontend/src/app/components/report-medical-history-editor/report-medical-history-editor.component.ts index fdb6c4a3..8a9f7bda 100644 --- a/frontend/src/app/components/report-medical-history-editor/report-medical-history-editor.component.ts +++ b/frontend/src/app/components/report-medical-history-editor/report-medical-history-editor.component.ts @@ -15,6 +15,9 @@ class RelatedNode { resource: ResourceFhir } +/** + * @deprecated This editor is no longer in use, the timeline panel allows users to tag resources associated with a single condition. + */ @Component({ selector: 'app-report-medical-history-editor', templateUrl: './report-medical-history-editor.component.html', diff --git a/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.html b/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.html index 81cdf80b..389b765e 100644 --- a/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.html +++ b/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.html @@ -31,7 +31,7 @@
    - + @@ -79,14 +79,14 @@ {{medication.display }} - +
  • {{medication.title}} - +
  • @@ -103,7 +103,7 @@ {{procedure.display}} - + @@ -117,7 +117,7 @@ {{diagnosticReport.title}} - + @@ -130,7 +130,7 @@ {{documentReference.sort_title}} - + diff --git a/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.ts b/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.ts index acd5f988..1789334e 100644 --- a/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.ts +++ b/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.ts @@ -15,6 +15,10 @@ import {DeviceModel} from '../../../lib/models/resources/device-model'; import {CodingModel} from '../../../lib/models/datatypes/coding-model'; import {LocationModel} from '../../../lib/models/resources/location-model'; +/** + * @deprecated This EOB panel is no longer in use, the timeline panel allows users to view resources by encounter + * TODO: this logic should be moved to the timeline panel before removal (Timeline doesnt have a visualization for EOBs) + */ @Component({ selector: 'app-report-medical-history-explanation-of-benefit', templateUrl: './report-medical-history-explanation-of-benefit.component.html', diff --git a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html index b7526173..ed57edc3 100644 --- a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html +++ b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html @@ -1,7 +1,7 @@
    - {{displayModel.period_start | amDateFormat:'YYYY' }} - {{displayModel.period_start | amDateFormat:'MMM DD' }} + {{displayModel?.period_start | amDateFormat:'YYYY' }} + {{displayModel?.period_start | amDateFormat:'MMM DD' }}
    @@ -14,7 +14,7 @@
    - {{displayModel.sort_title}} + {{displayModel?.sort_title}} @@ -23,24 +23,24 @@
    - + {{practitioner.sort_title}} - + - + {{organization.sort_title}} - + - + {{location.sort_title}} - + @@ -53,14 +53,14 @@ {{medication.display }} - +
  • {{medication.title }} - +
  • @@ -74,7 +74,7 @@ {{procedure.display}} - + @@ -87,7 +87,7 @@ {{immunization.sort_title}} - + @@ -109,11 +109,11 @@ @@ -146,7 +146,7 @@
    - +
    diff --git a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.spec.ts b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.spec.ts index 26935c04..2080b1e6 100644 --- a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.spec.ts +++ b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.spec.ts @@ -1,6 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReportMedicalHistoryTimelinePanelComponent } from './report-medical-history-timeline-panel.component'; +import {MomentModule} from 'ngx-moment'; describe('ReportMedicalHistoryTimelinePanelComponent', () => { let component: ReportMedicalHistoryTimelinePanelComponent; @@ -8,6 +9,7 @@ describe('ReportMedicalHistoryTimelinePanelComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ + imports: [ MomentModule ], declarations: [ ReportMedicalHistoryTimelinePanelComponent ] }) .compileComponents(); diff --git a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.ts b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.ts index 83881c4a..a4fdae62 100644 --- a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.ts +++ b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.ts @@ -2,13 +2,11 @@ import {Component, Input, OnInit} from '@angular/core'; import {ResourceFhir} from '../../models/fasten/resource_fhir'; import {EncounterModel} from '../../../lib/models/resources/encounter-model'; import {RecResourceRelatedDisplayModel} from '../../../lib/utils/resource_related_display_model'; -import {LocationModel} from '../../../lib/models/resources/location-model'; -import {PractitionerModel} from '../../../lib/models/resources/practitioner-model'; -import {OrganizationModel} from '../../../lib/models/resources/organization-model'; -import {fhirModelFactory} from '../../../lib/models/factory'; -import {ResourceType} from '../../../lib/models/constants'; import {DiagnosticReportModel} from '../../../lib/models/resources/diagnostic-report-model'; import {FastenDisplayModel} from '../../../lib/models/fasten/fasten-display-model'; +import {MedicalRecordWizardComponent} from '../medical-record-wizard/medical-record-wizard.component'; +import {NgbModal} from '@ng-bootstrap/ng-bootstrap'; +import {ReportMedicalHistoryEditorComponent} from '../report-medical-history-editor/report-medical-history-editor.component'; @Component({ selector: 'app-report-medical-history-timeline-panel', @@ -19,7 +17,7 @@ export class ReportMedicalHistoryTimelinePanelComponent implements OnInit { @Input() resourceFhir: ResourceFhir displayModel: EncounterModel - constructor() { } + constructor(private modalService: NgbModal) { } ngOnInit(): void { @@ -28,11 +26,21 @@ export class ReportMedicalHistoryTimelinePanelComponent implements OnInit { } diagnosticReportLink(diagnosticReportRaw: FastenDisplayModel): string { - console.log('diagnosticReportRaw', diagnosticReportRaw) let diagnosticReport = diagnosticReportRaw as DiagnosticReportModel return diagnosticReport?.is_category_lab_report ? '/labs/report/'+ diagnosticReport?.source_id + '/' + diagnosticReport?.source_resource_type + '/' + diagnosticReport?.source_resource_id : '/explore/'+ diagnosticReport?.source_id + '/resource/' + diagnosticReport?.source_resource_id + '/' } + + openMedicalRecordWizard(): void { + + + const modalRef = this.modalService.open(MedicalRecordWizardComponent, { + // const modalRef = this.modalService.open(ReportMedicalHistoryEditorComponent, { + size: 'xl', + }); + modalRef.componentInstance.existingEncounter = this.displayModel; + } + } diff --git a/frontend/src/app/components/resource-list/resource-list.component.html b/frontend/src/app/components/resource-list/resource-list.component.html deleted file mode 100644 index 8a3b0c18..00000000 --- a/frontend/src/app/components/resource-list/resource-list.component.html +++ /dev/null @@ -1,3 +0,0 @@ -

    {{resourceListType}}

    - - diff --git a/frontend/src/app/components/resource-list/resource-list.component.ts b/frontend/src/app/components/resource-list/resource-list.component.ts deleted file mode 100644 index 727eb498..00000000 --- a/frontend/src/app/components/resource-list/resource-list.component.ts +++ /dev/null @@ -1,174 +0,0 @@ -import {ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, SimpleChanges, Type, ViewChild} from '@angular/core'; -import {FastenApiService} from '../../services/fasten-api.service'; -import {Source} from '../../models/fasten/source'; -import {Observable, of} from 'rxjs'; -import {ResourceFhir} from '../../models/fasten/resource_fhir'; -import {ListAdverseEventComponent} from '../list-generic-resource/list-adverse-event.component'; -import {ListAllergyIntoleranceComponent} from '../list-generic-resource/list-allergy-intolerance.component'; -import {ListAppointmentComponent} from '../list-generic-resource/list-appointment.component'; -import {ListBinaryComponent} from '../list-generic-resource/list-binary.component'; -import {ListCarePlanComponent} from '../list-generic-resource/list-care-plan.component'; -import {ListCareTeamComponent} from '../list-generic-resource/list-care-team.component'; -import {ListCommunicationComponent} from '../list-generic-resource/list-communication.component'; -import {ListConditionComponent} from '../list-generic-resource/list-condition.component'; -import {ListCoverageComponent} from '../list-generic-resource/list-coverage.component'; -import {ListDeviceComponent} from '../list-generic-resource/list-device.component'; -import {ListDeviceRequestComponent} from '../list-generic-resource/list-device-request.component'; -import {ListDiagnosticReportComponent} from '../list-generic-resource/list-diagnostic-report.component'; -import {ListDocumentReferenceComponent} from '../list-generic-resource/list-document-reference.component'; -import {ListEncounterComponent} from '../list-generic-resource/list-encounter.component'; -import {ListFallbackComponent} from '../list-generic-resource/list-fallback.component'; -import {ListGenericResourceComponent, ResourceListComponentInterface} from '../list-generic-resource/list-generic-resource.component'; -import {ListGoalComponent} from '../list-generic-resource/list-goal.component'; -import {ListImmunizationComponent} from '../list-generic-resource/list-immunization.component'; -import {ListLocationComponent} from '../list-generic-resource/list-location.component'; -import {ListMedicationAdministrationComponent} from '../list-generic-resource/list-medication-administration.component'; -import {ListMedicationComponent} from '../list-generic-resource/list-medication.component'; -import {ListMedicationDispenseComponent} from '../list-generic-resource/list-medication-dispense.component'; -import {ListMedicationRequestComponent} from '../list-generic-resource/list-medication-request.component'; -import {ListNutritionOrderComponent} from '../list-generic-resource/list-nutrition-order.component'; -import {ListObservationComponent} from '../list-generic-resource/list-observation.component'; -import {ListOrganizationComponent} from '../list-generic-resource/list-organization.component'; -import {ListPractitionerComponent} from '../list-generic-resource/list-practitioner.component'; -import {ListProcedureComponent} from '../list-generic-resource/list-procedure.component'; -import {ListServiceRequestComponent} from '../list-generic-resource/list-service-request.component'; -import {ResourceListOutletDirective} from './resource-list-outlet.directive'; -import {ResponseWrapper} from '../../models/response-wrapper'; -import {map} from 'rxjs/operators'; - -@Component({ - selector: 'source-resource-list', - changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './resource-list.component.html', - styleUrls: ['./resource-list.component.scss'] -}) -export class ResourceListComponent implements OnInit, OnChanges { - - @Input() source: Source; - @Input() resourceListType: string; - @Input() selectedTotalElements: number; - - //location to dynamically load the resource list - @ViewChild(ResourceListOutletDirective, {static: true}) resourceListOutlet!: ResourceListOutletDirective; - - - constructor(private fastenApi: FastenApiService) { } - - ngOnInit(): void { - this.loadComponent() - } - ngOnChanges(changes: SimpleChanges) { - this.loadComponent() - } - - loadComponent() { - //clear the current outlet - const viewContainerRef = this.resourceListOutlet.viewContainerRef; - viewContainerRef.clear(); - - let componentType = this.typeLookup(this.resourceListType) - if(componentType != null){ - console.log("Attempting to create component", this.resourceListType, componentType) - const componentRef = viewContainerRef.createComponent(componentType); - componentRef.instance.totalElements = this.selectedTotalElements; - componentRef.instance.resourceListType = this.resourceListType; - componentRef.instance.sourceId = this.source.id; - componentRef.instance.markForCheck() - } - } - - typeLookup(resourceType: string): Type { - if(!resourceType){ - //dont try to render anything if the resourceType isnt set. - return null - } - switch(resourceType) { - case "Appointment": { - return ListAppointmentComponent; - } - case "AllergyIntolerance": { - return ListAllergyIntoleranceComponent; - } - case "AdverseEvent": { - return ListAdverseEventComponent; - } - case "Binary": { - return ListBinaryComponent; - } - case "CarePlan": { - return ListCarePlanComponent; - } - case "CareTeam": { - return ListCareTeamComponent; - } - case "Communication": { - return ListCommunicationComponent; - } - case "Condition": { - return ListConditionComponent; - } - case "Coverage": { - return ListCoverageComponent; - } - case "Device": { - return ListDeviceComponent; - } - case "DeviceRequest": { - return ListDeviceRequestComponent; - } - case "DiagnosticReport": { - return ListDiagnosticReportComponent; - } - case "DocumentReference": { - return ListDocumentReferenceComponent; - } - case "Encounter": { - return ListEncounterComponent; - } - case "Goal": { - return ListGoalComponent; - } - case "Immunization": { - return ListImmunizationComponent; - } - case "Location": { - return ListLocationComponent; - } - case "Medication": { - return ListMedicationComponent; - } - case "MedicationAdministration": { - return ListMedicationAdministrationComponent; - } - case "MedicationDispense": { - return ListMedicationDispenseComponent; - } - case "MedicationRequest": { - return ListMedicationRequestComponent; - } - case "NutritionOrder": { - return ListNutritionOrderComponent; - } - case "Observation": { - return ListObservationComponent; - } - case "Organization": { - return ListOrganizationComponent; - } - case "Practitioner": { - return ListPractitionerComponent; - } - case "Procedure": { - return ListProcedureComponent; - } - case "ServiceRequest": { - return ListServiceRequestComponent; - } - default: { - console.warn("Unknown component type, using fallback", resourceType) - return ListFallbackComponent; - - } - } - } -} diff --git a/frontend/src/app/components/shared.module.ts b/frontend/src/app/components/shared.module.ts index f3e05b9d..d24ef2e8 100644 --- a/frontend/src/app/components/shared.module.ts +++ b/frontend/src/app/components/shared.module.ts @@ -1,91 +1,40 @@ import { ComponentsSidebarComponent } from './components-sidebar/components-sidebar.component'; -import { ListGenericResourceComponent,} from './list-generic-resource/list-generic-resource.component'; -import { ListPatientComponent } from './list-patient/list-patient.component'; import { NgModule } from '@angular/core'; import { NgxDatatableModule } from '@swimlane/ngx-datatable'; import { RouterModule } from '@angular/router'; import { UtilitiesSidebarComponent } from './utilities-sidebar/utilities-sidebar.component'; import {BrowserModule} from '@angular/platform-browser'; -import { AllergyIntoleranceComponent } from './fhir/resources/allergy-intolerance/allergy-intolerance.component'; -import { BadgeComponent } from './fhir/common/badge/badge.component'; -import { BinaryComponent } from './fhir/resources/binary/binary.component'; -import { BinaryTextComponent } from './fhir/datatypes/binary-text/binary-text.component'; -import { CodableConceptComponent } from './fhir/datatypes/codable-concept/codable-concept.component'; -import { CodingComponent } from './fhir/datatypes/coding/coding.component'; -import { DiagnosticReportComponent } from './fhir/resources/diagnostic-report/diagnostic-report.component'; -import { DicomComponent } from './fhir/datatypes/dicom/dicom.component'; -import { DocumentReferenceComponent } from './fhir/resources/document-reference/document-reference.component'; -import { FallbackComponent } from './fhir/resources/fallback/fallback.component'; -import { FhirResourceComponent } from './fhir/fhir-resource/fhir-resource.component'; -import { FhirResourceOutletDirective } from './fhir/fhir-resource/fhir-resource-outlet.directive'; import { GlossaryLookupComponent } from './glossary-lookup/glossary-lookup.component'; -import { HtmlComponent } from './fhir/datatypes/html/html.component'; -import { ImgComponent } from './fhir/datatypes/img/img.component'; -import { ImmunizationComponent } from './fhir/resources/immunization/immunization.component'; import { LoadingSpinnerComponent } from './loading-spinner/loading-spinner.component'; -import { MarkdownComponent } from './fhir/datatypes/markdown/markdown.component'; -import { MediaComponent } from './fhir/resources/media/media.component'; import { MedicalSourcesCardComponent } from './medical-sources-card/medical-sources-card.component'; import { MedicalSourcesCategoryLookupPipe } from './medical-sources-filter/medical-sources-category-lookup.pipe'; import { MedicalSourcesConnectedComponent } from './medical-sources-connected/medical-sources-connected.component'; import { MedicalSourcesFilterComponent } from './medical-sources-filter/medical-sources-filter.component'; -import { MedicationComponent } from './fhir/resources/medication/medication.component'; -import { MedicationRequestComponent } from './fhir/resources/medication-request/medication-request.component'; import { MomentModule } from 'ngx-moment'; import { NgChartsModule } from 'ng2-charts'; import { NlmTypeaheadComponent } from './nlm-typeahead/nlm-typeahead.component'; -import { PdfComponent } from './fhir/datatypes/pdf/pdf.component'; -import { PractitionerComponent } from './fhir/resources/practitioner/practitioner.component'; -import { ProcedureComponent } from './fhir/resources/procedure/procedure.component'; import { ReportHeaderComponent } from './report-header/report-header.component'; import { ReportLabsObservationComponent } from './report-labs-observation/report-labs-observation.component'; import { ReportMedicalHistoryConditionComponent } from './report-medical-history-condition/report-medical-history-condition.component'; import { ReportMedicalHistoryEditorComponent } from './report-medical-history-editor/report-medical-history-editor.component'; import { ReportMedicalHistoryExplanationOfBenefitComponent } from './report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component'; -import { ResourceListComponent } from './resource-list/resource-list.component'; -import { TableComponent } from './fhir/common/table/table.component'; import { ToastComponent } from './toast/toast.component'; import { TreeModule } from '@circlon/angular-tree-component'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {GridstackComponent} from './gridstack/gridstack.component'; import {GridstackItemComponent} from './gridstack/gridstack-item.component'; import {HighlightModule} from 'ngx-highlightjs'; -import {ListAdverseEventComponent} from './list-generic-resource/list-adverse-event.component'; -import {ListAllergyIntoleranceComponent} from './list-generic-resource/list-allergy-intolerance.component'; -import {ListAppointmentComponent} from './list-generic-resource/list-appointment.component'; -import {ListBinaryComponent} from './list-generic-resource/list-binary.component'; -import {ListCarePlanComponent} from './list-generic-resource/list-care-plan.component'; -import {ListCareTeamComponent} from './list-generic-resource/list-care-team.component'; -import {ListCommunicationComponent} from './list-generic-resource/list-communication.component'; -import {ListConditionComponent} from './list-generic-resource/list-condition.component' -import {ListCoverageComponent} from './list-generic-resource/list-coverage.component'; -import {ListDeviceComponent} from './list-generic-resource/list-device.component'; -import {ListDeviceRequestComponent} from './list-generic-resource/list-device-request.component'; -import {ListDiagnosticReportComponent} from './list-generic-resource/list-diagnostic-report.component'; -import {ListDocumentReferenceComponent} from './list-generic-resource/list-document-reference.component'; -import {ListEncounterComponent} from './list-generic-resource/list-encounter.component' -import {ListFallbackComponent} from './list-generic-resource/list-fallback.component' -import {ListGoalComponent} from './list-generic-resource/list-goal.component'; -import {ListImmunizationComponent} from './list-generic-resource/list-immunization.component' -import {ListLocationComponent} from './list-generic-resource/list-location.component' -import {ListMedicationAdministrationComponent} from './list-generic-resource/list-medication-administration.component'; -import {ListMedicationComponent} from './list-generic-resource/list-medication.component' -import {ListMedicationDispenseComponent} from './list-generic-resource/list-medication-dispense.component'; -import {ListMedicationRequestComponent} from './list-generic-resource/list-medication-request.component' -import {ListNutritionOrderComponent} from './list-generic-resource/list-nutrition-order.component'; -import {ListObservationComponent} from './list-generic-resource/list-observation.component' -import {ListOrganizationComponent} from './list-generic-resource/list-organization.component' -import {ListPractitionerComponent} from './list-generic-resource/list-practitioner.component' -import {ListProcedureComponent} from './list-generic-resource/list-procedure.component' -import {ListServiceRequestComponent} from './list-generic-resource/list-service-request.component'; -import {NgbCollapseModule, NgbModule, NgbDropdownModule, NgbAccordionModule} from '@ng-bootstrap/ng-bootstrap'; +import {NgbCollapseModule, NgbModule, NgbDropdownModule, NgbAccordionModule, NgbNavModule} from '@ng-bootstrap/ng-bootstrap'; import {PipesModule} from '../pipes/pipes.module'; -import {ResourceListOutletDirective} from './resource-list/resource-list-outlet.directive'; import {DirectivesModule} from '../directives/directives.module'; import { ReportMedicalHistoryTimelinePanelComponent } from './report-medical-history-timeline-panel/report-medical-history-timeline-panel.component'; -import { OrganizationComponent } from './fhir/resources/organization/organization.component'; -import { LocationComponent } from './fhir/resources/location/location.component'; -import { ObservationComponent } from './fhir/resources/observation/observation.component'; +import { MedicalRecordWizardComponent } from './medical-record-wizard/medical-record-wizard.component'; +import { MedicalRecordWizardAddPractitionerComponent } from './medical-record-wizard-add-practitioner/medical-record-wizard-add-practitioner.component'; +import { MedicalRecordWizardAddOrganizationComponent } from './medical-record-wizard-add-organization/medical-record-wizard-add-organization.component'; +import { MedicalRecordWizardAddAttachmentComponent } from './medical-record-wizard-add-attachment/medical-record-wizard-add-attachment.component'; +import {FhirCardModule} from './fhir-card/fhir-card.module'; +import {FhirDatatableModule} from './fhir-datatable/fhir-datatable.module'; +import { MedicalRecordWizardAddEncounterComponent } from './medical-record-wizard-add-encounter/medical-record-wizard-add-encounter.component'; @NgModule({ imports: [ @@ -96,6 +45,7 @@ import { ObservationComponent } from './fhir/resources/observation/observation.c NgbDropdownModule, NgbCollapseModule, NgbAccordionModule, + NgbNavModule, FormsModule, ReactiveFormsModule, MomentModule, @@ -104,69 +54,25 @@ import { ObservationComponent } from './fhir/resources/observation/observation.c HighlightModule, PipesModule, DirectivesModule, + FhirCardModule, + FhirDatatableModule, //standalone components - AllergyIntoleranceComponent, - BadgeComponent, - BinaryComponent, - BinaryTextComponent, - CodableConceptComponent, - CodingComponent, - DicomComponent, GlossaryLookupComponent, GridstackComponent, GridstackItemComponent, - HtmlComponent, - ImgComponent, - ImmunizationComponent, LoadingSpinnerComponent, - LocationComponent, - MarkdownComponent, - MedicationComponent, - MedicationRequestComponent, - OrganizationComponent, - ObservationComponent, - PdfComponent, - PractitionerComponent, - ProcedureComponent, - TableComponent, + MedicalRecordWizardComponent, + NlmTypeaheadComponent, + MedicalRecordWizardAddPractitionerComponent, + MedicalRecordWizardAddOrganizationComponent, + MedicalRecordWizardAddAttachmentComponent, + MedicalRecordWizardAddEncounterComponent, ], declarations: [ ComponentsSidebarComponent, UtilitiesSidebarComponent, - ListAdverseEventComponent, - ListAllergyIntoleranceComponent, - ListAppointmentComponent, - ListBinaryComponent, - ListCarePlanComponent, - ListCareTeamComponent, - ListCommunicationComponent, - ListConditionComponent, - ListCoverageComponent, - ListDeviceComponent, - ListDeviceRequestComponent, - ListDiagnosticReportComponent, - ListDocumentReferenceComponent, - ListEncounterComponent, - ListGenericResourceComponent, - ListGoalComponent, - ListImmunizationComponent, - ListLocationComponent, - ListMedicationAdministrationComponent, - ListMedicationComponent, - ListMedicationDispenseComponent, - ListMedicationRequestComponent, - ListNutritionOrderComponent, - ListObservationComponent, - ListOrganizationComponent, - ListPatientComponent, - ListPractitionerComponent, - ListProcedureComponent, - ListServiceRequestComponent, - ResourceListComponent, - ResourceListOutletDirective, - ListFallbackComponent, ToastComponent, ReportHeaderComponent, ReportMedicalHistoryEditorComponent, @@ -174,14 +80,6 @@ import { ObservationComponent } from './fhir/resources/observation/observation.c ReportLabsObservationComponent, ReportMedicalHistoryTimelinePanelComponent, - FhirResourceComponent, - FhirResourceOutletDirective, - FallbackComponent, - ListFallbackComponent, - DiagnosticReportComponent, - NlmTypeaheadComponent, - DocumentReferenceComponent, - MediaComponent, ReportMedicalHistoryExplanationOfBenefitComponent, MedicalSourcesFilterComponent, MedicalSourcesConnectedComponent, @@ -189,56 +87,14 @@ import { ObservationComponent } from './fhir/resources/observation/observation.c MedicalSourcesCardComponent, ], exports: [ - BinaryComponent, ComponentsSidebarComponent, - DiagnosticReportComponent, - DocumentReferenceComponent, - FallbackComponent, - ListFallbackComponent, - FhirResourceComponent, - FhirResourceOutletDirective, - ImmunizationComponent, - ListAdverseEventComponent, - ListAllergyIntoleranceComponent, - ListAppointmentComponent, - ListBinaryComponent, - ListCarePlanComponent, - ListCareTeamComponent, - ListCommunicationComponent, - ListConditionComponent, - ListCoverageComponent, - ListDeviceComponent, - ListDeviceRequestComponent, - ListDiagnosticReportComponent, - ListDocumentReferenceComponent, - ListEncounterComponent, - ListGenericResourceComponent, - ListGoalComponent, - ListImmunizationComponent, - ListLocationComponent, - ListMedicationAdministrationComponent, - ListMedicationComponent, - ListMedicationDispenseComponent, - ListMedicationRequestComponent, - ListNutritionOrderComponent, - ListObservationComponent, - ListOrganizationComponent, - ListPatientComponent, - ListPractitionerComponent, - ListProcedureComponent, - ListServiceRequestComponent, MedicalSourcesFilterComponent, - MedicationRequestComponent, NlmTypeaheadComponent, - PractitionerComponent, - ProcedureComponent, ReportHeaderComponent, ReportLabsObservationComponent, ReportMedicalHistoryConditionComponent, ReportMedicalHistoryEditorComponent, ReportMedicalHistoryExplanationOfBenefitComponent, - ResourceListComponent, - ResourceListOutletDirective, ToastComponent, UtilitiesSidebarComponent, MedicalSourcesCardComponent, @@ -246,26 +102,17 @@ import { ObservationComponent } from './fhir/resources/observation/observation.c ReportMedicalHistoryTimelinePanelComponent, //standalone components - AllergyIntoleranceComponent, - BadgeComponent, - BinaryComponent, - CodableConceptComponent, - CodingComponent, - GlossaryLookupComponent, - GridstackComponent, - GridstackItemComponent, - ImmunizationComponent, - LoadingSpinnerComponent, - LocationComponent, - MedicalSourcesCategoryLookupPipe, - MedicationComponent, - MedicationRequestComponent, - OrganizationComponent, - ObservationComponent, - PractitionerComponent, - ProcedureComponent, - TableComponent, - + GlossaryLookupComponent, + GridstackComponent, + GridstackItemComponent, + LoadingSpinnerComponent, + MedicalRecordWizardAddAttachmentComponent, + MedicalRecordWizardAddEncounterComponent, + MedicalRecordWizardAddOrganizationComponent, + MedicalRecordWizardAddPractitionerComponent, + MedicalRecordWizardComponent, + MedicalSourcesCategoryLookupPipe, + NlmTypeaheadComponent, ] }) diff --git a/frontend/src/app/models/fasten/resource_create.ts b/frontend/src/app/models/fasten/resource_create.ts index 3abf4631..3830f5dd 100644 --- a/frontend/src/app/models/fasten/resource_create.ts +++ b/frontend/src/app/models/fasten/resource_create.ts @@ -1,6 +1,10 @@ import {IResourceRaw} from './resource_fhir'; import {CodingModel} from '../../../lib/models/datatypes/coding-model'; import {NlmSearchResults} from '../../services/nlm-clinical-table-search.service'; +import {WizardFhirResourceWrapper} from '../../components/medical-record-wizard/medical-record-wizard.utilities'; +import {EncounterModel} from '../../../lib/models/resources/encounter-model'; +import {PractitionerModel} from '../../../lib/models/resources/practitioner-model'; +import {OrganizationModel} from '../../../lib/models/resources/organization-model'; // @@ -124,12 +128,12 @@ import {NlmSearchResults} from '../../services/nlm-clinical-table-search.service // ] // } -export interface ResourceCreate { - condition: ResourceCreateCondition, +export interface MedicalRecordWizardFormCreate { + encounter: WizardFhirResourceWrapper, "medications": ResourceCreateMedication[], "procedures": ResourceCreateProcedure[], - "practitioners": ResourceCreatePractitioner[], - "organizations": ResourceCreateOrganization[], + "practitioners": WizardFhirResourceWrapper[], + "organizations": WizardFhirResourceWrapper[], "attachments": ResourceCreateAttachment[], } @@ -168,6 +172,7 @@ export interface ResourceCreateProcedure { "attachments": ResourceCreateAttachment[], } +// @deprecated - replaced by PractitionerModel display model export interface ResourceCreatePractitioner { "id"?: string, "identifier": CodingModel[] @@ -179,6 +184,7 @@ export interface ResourceCreatePractitioner { "address": Address, } +// @deprecated - replaced by OrganizationModel display model export interface ResourceCreateOrganization { "id"?: string, "identifier": CodingModel[] diff --git a/frontend/src/app/pages/dashboard/dashboard.component.ts b/frontend/src/app/pages/dashboard/dashboard.component.ts index 6337b4fb..e3db0796 100644 --- a/frontend/src/app/pages/dashboard/dashboard.component.ts +++ b/frontend/src/app/pages/dashboard/dashboard.component.ts @@ -85,7 +85,7 @@ export class DashboardComponent implements OnInit { } isActive(source: Source){ - if(source.source_type == "manual"){ + if(source.source_type == "manual" || source.source_type == 'fasten'){ return '--' } let expiresDate = new Date(source.expires_at); diff --git a/frontend/src/app/pages/medical-history/medical-history.component.spec.ts b/frontend/src/app/pages/medical-history/medical-history.component.spec.ts index cc26be53..eaad1839 100644 --- a/frontend/src/app/pages/medical-history/medical-history.component.spec.ts +++ b/frontend/src/app/pages/medical-history/medical-history.component.spec.ts @@ -13,7 +13,7 @@ describe('MedicalHistoryComponent', () => { beforeEach(async () => { - mockedFastenApiService = jasmine.createSpyObj('FastenApiService', ['getResourceGraph']) + mockedFastenApiService = jasmine.createSpyObj('FastenApiService', ['getResources', 'getResourceGraph']) await TestBed.configureTestingModule({ declarations: [ MedicalHistoryComponent ], providers: [{ @@ -23,6 +23,7 @@ describe('MedicalHistoryComponent', () => { }) .compileComponents(); mockedFastenApiService.getResourceGraph.and.returnValue(of({"Condition":[],"Encounter":[]})); + mockedFastenApiService.getResources.and.returnValue(of([])); fixture = TestBed.createComponent(MedicalHistoryComponent); component = fixture.componentInstance; diff --git a/frontend/src/app/pages/medical-history/medical-history.component.ts b/frontend/src/app/pages/medical-history/medical-history.component.ts index 8f46696a..8e5f68cf 100644 --- a/frontend/src/app/pages/medical-history/medical-history.component.ts +++ b/frontend/src/app/pages/medical-history/medical-history.component.ts @@ -30,7 +30,7 @@ export class MedicalHistoryComponent implements OnInit { constructor( - private fastenApi: FastenApiService, + public fastenApi: FastenApiService, private modalService: NgbModal ) { } diff --git a/frontend/src/app/pages/medical-sources/medical-sources.component.html b/frontend/src/app/pages/medical-sources/medical-sources.component.html index 92a19df3..2829f13a 100644 --- a/frontend/src/app/pages/medical-sources/medical-sources.component.html +++ b/frontend/src/app/pages/medical-sources/medical-sources.component.html @@ -100,7 +100,7 @@ class="mg-b-0" externalLink>{{modalSelectedSourceListItem?.metadata.patient_access_url | shortDomain}}
    - diff --git a/frontend/src/app/pages/patient-profile/patient-profile.component.html b/frontend/src/app/pages/patient-profile/patient-profile.component.html index 2579d100..d029af66 100644 --- a/frontend/src/app/pages/patient-profile/patient-profile.component.html +++ b/frontend/src/app/pages/patient-profile/patient-profile.component.html @@ -150,7 +150,7 @@ - +

    @@ -166,7 +166,7 @@
    - + diff --git a/frontend/src/app/pages/report-labs/report-labs.component.html b/frontend/src/app/pages/report-labs/report-labs.component.html index d54c1438..3d9dba3e 100644 --- a/frontend/src/app/pages/report-labs/report-labs.component.html +++ b/frontend/src/app/pages/report-labs/report-labs.component.html @@ -15,7 +15,7 @@

    Report Info

    - +
    diff --git a/frontend/src/app/pages/resource-creator/resource-creator.component.html b/frontend/src/app/pages/resource-creator/resource-creator.component.html index 201e4152..2b269b6f 100644 --- a/frontend/src/app/pages/resource-creator/resource-creator.component.html +++ b/frontend/src/app/pages/resource-creator/resource-creator.component.html @@ -8,649 +8,7 @@

    Create a Record

    - -
    -
    - - - -
    -
    - Form Status: {{ form.status }} -
    -
    -
    - - -

    - A condition is a disease, illness, or injury that needs to be managed over time. A condition may be a comorbidity (a co-occurring condition), or it may be a main diagnosis. -

    - - - -
    - Condition Status: {{form.get('condition').status}} -
    - -
    -
    -

    Medical condition*

    - -
    -
    -

    Status*

    - -
    -
    -

    Started*

    - -
    -
    -

    Stopped

    - -
    -
    - -
    -
    -

    Description/Comment

    - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    Medications
    -

    - Medications are substances that are taken to treat or prevent disease or illness. Medications are generally characterized by their chemical composition and the way they are administered. -

    -
    -
    -
    - - -
    -
    - Medication Status: {{medicationGroup.status}} -
    -
    - -
    -
    -
    -

    Medication name*

    - -
    -
    -

    Status*

    -
    -
    -

    Dosage

    - -
    -
    -

    Started*

    - -
    -
    -

    Stopped

    - -
    -
    -

    Why Stopped

    - -
    -
    -

    Prescribing Practitioner*

    - -
    -
    -
    -
    -

    Instructions

    - -
    -
    -
    -
    -

    Attachments

    -
    -
    - -
    -
    - - {{attachment.value.name}} ({{attachment.value.file_name}}) - -
    -
    -
    -
    -
    -
    - -
    -
    -
    - -
    -
    -
    - -
    - - -
    -
    -
    -
    -
    -
    Major Surgeries and Implants
    -

    - Implants, devices, major surgeries and other procedures that are part of a patient's history. -

    -
    -
    -
    - -
    -
    - Procedure Status: {{procedureGroup.status}} -
    -
    - -
    -
    -
    -

    Surgery or Implant*

    - -
    -
    -

    When done*

    - -
    -
    -

    Performed By

    - -
    -
    -

    Location

    - -
    -
    -

    Comments

    - -
    -
    -
    -
    -

    Attachments

    -
    - -
    - -
    -
    - - {{attachment.value.name}} ({{attachment.value.file_name}}) - -
    -
    -
    -
    - - -
    -
    - -
    -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    Medical Practitioners
    -

    Practitioners involved in the care of the patient.

    -
    -
    -
    - - -
    -
    - Practitioner Status: {{practitionerGroup.status}} -
    -
    - -
    - -
    - - -
    -

    Name*

    - -
    -
    -

    Type*

    - -
    -
    -

    Telephone

    - -
    -
    -

    Fax

    - -
    -
    -

    Email

    - -
    -
    -

    Address

    - -
    - -
    - -
    -
    - -
    -
    - -
    -
    -
    - - -
    -
    -
    - -
    - - -
    -
    -
    -
    -
    -
    Medical Location/Organizations
    -

    Locations and Organizations involved in the care of the patient. -

    -
    -
    -
    - - -
    -
    - Organization Status: {{organizationGroup.status}} -
    -
    - -
    - -
    - - -
    -

    Name*

    - -
    -
    -

    Type

    - -
    -
    -

    Telephone

    - -
    -
    -

    Fax

    - -
    -
    -

    Email

    - -
    - -
    - -
    -
    - -
    -
    - -
    -
    -
    - - -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    Notes & Attachments
    -

    - Files and notes related to medications, procedures, or the current condition. -

    -
    -
    -
    - - -
    -
    - Attachment Status: {{attachmentGroup.status}} -
    -
    - -
    - -
    - - -
    -

    Name*

    - -
    -
    -

    Category

    - -
    -
    -

    File Type

    - -
    -
    - -
    -
    - - - - -
    -
    - -
    -
    -
    - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/app/pages/resource-creator/resource-creator.component.ts b/frontend/src/app/pages/resource-creator/resource-creator.component.ts index b7a3d487..07040936 100644 --- a/frontend/src/app/pages/resource-creator/resource-creator.component.ts +++ b/frontend/src/app/pages/resource-creator/resource-creator.component.ts @@ -1,464 +1,30 @@ -import {Component, Input, OnInit} from '@angular/core'; -import {AbstractControl, FormArray, FormControl, FormGroup, Validators} from '@angular/forms'; -import { ModalDismissReasons, NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { - ResourceCreateAttachment, - ResourceCreateOrganization, - ResourceCreatePractitioner, -} from '../../models/fasten/resource_create'; -import {uuidV4} from '../../../lib/utils/uuid'; -import {NlmSearchResults} from '../../services/nlm-clinical-table-search.service'; -import {GenerateR4Bundle} from './resource-creator.utilities'; -import {FastenApiService} from '../../services/fasten-api.service'; +import {Component, OnInit} from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import {MedicalRecordWizardComponent} from '../../components/medical-record-wizard/medical-record-wizard.component'; import {Router} from '@angular/router'; -export interface MedicationModel { - data: {}, - status: string, - dosage: string, - started: null, - stopped: null, - whystopped: string, - resupply: string -} - -export enum ContactType { - ContactTypeSearch = 'search', - ContactTypeManual = 'manual', -} - @Component({ selector: 'app-resource-creator', templateUrl: './resource-creator.component.html', styleUrls: ['./resource-creator.component.scss'] }) export class ResourceCreatorComponent implements OnInit { - debugMode = false; - collapsePanel: {[name: string]: boolean} = {} - - @Input() form!: FormGroup; - get isValid() { return true; } - - // model: any = { - // condition: { - // data: {}, - // status: null, - // started: null, - // stopped: null, - // description: null, - // }, - // medication: [] - // } - - - constructor(private router: Router, private modalService: NgbModal, private fastenApi: FastenApiService) { } + constructor( private modalService: NgbModal, private router: Router) { } ngOnInit(): void { - - //https://stackoverflow.com/questions/52038071/creating-nested-form-groups-using-angular-reactive-forms - //https://www.danywalls.com/creating-dynamic-forms-in-angular-a-step-by-step-guide - //https://www.telerik.com/blogs/angular-basics-creating-dynamic-forms-using-formarray-angular - //https://angular.io/guide/reactive-forms#creating-dynamic-forms - //https://angular.io/guide/dynamic-form - this.form = new FormGroup({ - condition: new FormGroup({ - data: new FormControl(null, Validators.required), - status: new FormControl(null, Validators.required), - started: new FormControl(null, Validators.required), - stopped: new FormControl(null), - description: new FormControl(null), - }), - - medications: new FormArray([]), - procedures: new FormArray([]), - practitioners: new FormArray([]), - organizations: new FormArray([]), - attachments: new FormArray([]), + const modalRef = this.modalService.open(MedicalRecordWizardComponent, { + // const modalRef = this.modalService.open(ReportMedicalHistoryEditorComponent, { + size: 'xl', }); - this.resetOrganizationForm() - // this.resetPractitionerForm() - } - - get medications(): FormArray { - return this.form.controls["medications"] as FormArray; - } - addMedication(){ - const medicationGroup = new FormGroup({ - data: new FormControl(null, Validators.required), - status: new FormControl(null, Validators.required), - dosage: new FormControl({ - value: '', disabled: true - }), - started: new FormControl(null, Validators.required), - stopped: new FormControl(null), - whystopped: new FormControl(null), - requester: new FormControl(null, Validators.required), - instructions: new FormControl(null), - attachments: new FormControl([]), - }); - - medicationGroup.get("data").valueChanges.subscribe(val => { - medicationGroup.get("dosage").enable(); - //TODO: find a way to create dependant dosage information based on medication data. - }); - - this.medications.push(medicationGroup); - } - deleteMedication(index: number) { - this.medications.removeAt(index); - } - - - get procedures(): FormArray { - return this.form.controls["procedures"] as FormArray; - } - addProcedure(){ - const procedureGroup = new FormGroup({ - data: new FormControl(null, Validators.required), - whendone: new FormControl(null, Validators.required), - performer: new FormControl(null), - location: new FormControl(null), - comment: new FormControl(''), - attachments: new FormControl([]), - }); - - this.procedures.push(procedureGroup); - } - deleteProcedure(index: number) { - this.procedures.removeAt(index); - } - - - get practitioners(): FormArray { - return this.form.controls["practitioners"] as FormArray; - } - - addPractitioner(practitioner: ResourceCreatePractitioner){ - const practitionerGroup = new FormGroup({ - id: new FormControl(practitioner.id, Validators.required), - identifier: new FormControl(practitioner.identifier), - profession: new FormControl(practitioner.profession, Validators.required), - name: new FormControl(practitioner.name, Validators.required), - phone: new FormControl(practitioner.phone, Validators.pattern('[- +()0-9]+')), - fax: new FormControl(practitioner.fax, Validators.pattern('[- +()0-9]+')), - email: new FormControl(practitioner.email, Validators.email), - address: new FormGroup({ - line1: new FormControl(practitioner.address.line1), - line2: new FormControl(practitioner.address.line2), - city: new FormControl(practitioner.address.city), - state: new FormControl(practitioner.address.state), - zip: new FormControl(practitioner.address.zip), - country: new FormControl(practitioner.address.country), - }), - }); - - this.practitioners.push(practitionerGroup); - } - - deletePractitioner(index: number) { - this.practitioners.removeAt(index); - } - - - get organizations(): FormArray { - return this.form.controls["organizations"] as FormArray; - } - - addOrganization(organization: ResourceCreateOrganization){ - const organizationGroup = new FormGroup({ - id: new FormControl(organization.id, Validators.required), - identifier: new FormControl(organization.identifier), - name: new FormControl(organization.name, Validators.required), - type: new FormControl(organization.type), - phone: new FormControl(organization.phone, Validators.pattern('[- +()0-9]+')), - fax: new FormControl(organization.fax, Validators.pattern('[- +()0-9]+')), - email: new FormControl(organization.email, Validators.email), - address: new FormControl(organization.address), - }); - - this.organizations.push(organizationGroup); - } - deleteOrganization(index: number) { - this.organizations.removeAt(index); - } - - get attachments(): FormArray { - return this.form.controls["attachments"] as FormArray; - } - - addAttachment(attachment: ResourceCreateAttachment){ - const attachmentGroup = new FormGroup({ - id: new FormControl(attachment.id, Validators.required), - name: new FormControl(attachment.name, Validators.required), - category: new FormControl(attachment.category, Validators.required), - file_type: new FormControl(attachment.file_type, Validators.required), - file_name: new FormControl(attachment.file_name, Validators.required), - file_content: new FormControl(attachment.file_content, Validators.required), - file_size: new FormControl(attachment.file_size), - }); - - this.attachments.push(attachmentGroup); - } - deleteAttachment(index: number) { - this.attachments.removeAt(index); - } - - - onSubmit() { - console.log(this.form.getRawValue()) - this.form.markAllAsTouched() - if (this.form.valid) { - console.log('form submitted'); - - let bundle = GenerateR4Bundle(this.form.getRawValue()); - - let bundleJsonStr = JSON.stringify(bundle); - let bundleBlob = new Blob([bundleJsonStr], { type: 'application/json' }); - let bundleFile = new File([ bundleBlob ], 'bundle.json'); - this.fastenApi.createManualSource(bundleFile).subscribe((resp) => { - console.log(resp) - this.router.navigate(['/medical-history']) - }) - - } - - - } - - //Modal Helpers - newPractitionerTypeaheadForm: FormGroup - newPractitionerForm: FormGroup //ResourceCreatePractitioner - - newOrganizationTypeaheadForm: FormGroup - newOrganizationForm: FormGroup //ResourceCreateOrganization - - newAttachmentForm: FormGroup - - openPractitionerModal(content, formGroup?: AbstractControl, controlName?: string) { - this.resetPractitionerForm() - this.modalService.open(content, { - ariaLabelledBy: 'modal-practitioner', - beforeDismiss: () => { - console.log("validate Practitioner form") - this.newPractitionerForm.markAllAsTouched() - this.newPractitionerTypeaheadForm.markAllAsTouched() - return this.newPractitionerForm.valid - }, - }).result.then( - () => { - console.log('Closed without saving'); - }, - () => { - console.log('Closing, saving form'); - //add this to the list of organization - let result = this.newPractitionerForm.getRawValue() - result.id = uuidV4(); - this.addPractitioner(result); - if(formGroup && controlName){ - //set this practitioner to the current select box - formGroup.get(controlName).setValue(result.id); - } - }, - ); - } - - openOrganizationModal(content, formGroup?: AbstractControl, controlName?: string) { - this.resetOrganizationForm() - - this.modalService.open(content, { - ariaLabelledBy: 'modal-organization', - beforeDismiss: () => { - console.log("validate Organization form") - this.newOrganizationForm.markAllAsTouched() - this.newOrganizationTypeaheadForm.markAllAsTouched() - return this.newOrganizationForm.valid - }, - }).result.then( - () => { - console.log('Closed without saving'); - }, - () => { - console.log('Closing, saving form'); - //add this to the list of organization - let result = this.newOrganizationForm.getRawValue() - result.id = uuidV4(); - this.addOrganization(result); - if(formGroup && controlName){ - //set this practitioner to the current select box - formGroup.get(controlName).setValue(result.id); - } - }, - ); - } - - openAttachmentModal(content, formGroup?: AbstractControl, controlName?: string) { - this.resetAttachmentForm() - - this.modalService.open(content, { - ariaLabelledBy: 'modal-attachment', - beforeDismiss: () => { - console.log("validate Attachment form") - this.newAttachmentForm.markAllAsTouched() - return this.newAttachmentForm.valid - }, - }).result.then( - () => { - console.log('Closed without saving'); - }, - () => { - console.log('Closing, saving form'); - //add this to the list of organization - let result = this.newAttachmentForm.getRawValue() - result.id = uuidV4(); - this.addAttachment(result); - - if(formGroup && controlName){ - - //add this attachment id to the current FormArray - let controlArrayVal = formGroup.get(controlName).getRawValue(); - controlArrayVal.push(result.id) - formGroup.get(controlName).setValue(controlArrayVal); - } - }, - ); - } - - private resetPractitionerForm(){ - this.newPractitionerTypeaheadForm = new FormGroup({ - data: new FormControl(null, Validators.required), - }) - this.newPractitionerTypeaheadForm.valueChanges.subscribe(form => { - console.log("CHANGE INDIVIDUAL IN MODAL", form) - let val = form.data - if(val.provider_type){ - this.newPractitionerForm.get('profession').setValue(val.provider_type) - } - if(val.identifier){ - this.newPractitionerForm.get('identifier').setValue( val.identifier); - } - if(form.data.provider_phone){ - this.newPractitionerForm.get('phone').setValue( val.provider_phone); - } - if(val.provider_fax){ - this.newPractitionerForm.get('fax').setValue(val.provider_fax); - } - - if(val.provider_address){ - let addressGroup = this.newPractitionerForm.get('address') - addressGroup.get('line1').setValue(val.provider_address.line1) - addressGroup.get('line2').setValue(val.provider_address.line2) - addressGroup.get('city').setValue(val.provider_address.city) - addressGroup.get('state').setValue(val.provider_address.state) - addressGroup.get('zip').setValue(val.provider_address.zip) - addressGroup.get('country').setValue(val.provider_address.country) - } - if(val.text) { - this.newPractitionerForm.get('name').setValue( val.text); - } - }); - - - this.newPractitionerForm = new FormGroup({ - identifier: new FormControl([]), - name: new FormControl(null, Validators.required), - profession: new FormControl(null, Validators.required), - phone: new FormControl(null, Validators.pattern('[- +()0-9]+')), - fax: new FormControl(null, Validators.pattern('[- +()0-9]+')), - email: new FormControl(null, Validators.email), - address: new FormGroup({ - line1: new FormControl(null), - line2: new FormControl(null), - city: new FormControl(null), - state: new FormControl(null), - zip: new FormControl(null), - country: new FormControl(null), - }) - }) - - - } - - private resetOrganizationForm(){ - this.newOrganizationTypeaheadForm = new FormGroup({ - data: new FormControl(null, Validators.required), - }) - this.newOrganizationTypeaheadForm.valueChanges.subscribe(form => { - console.log("CHANGE Organization IN MODAL", form) - let val = form.data - if(val.provider_type) { - this.newOrganizationForm.get('type').setValue(val.provider_type) - } - if(val.identifier){ - this.newOrganizationForm.get('identifier').setValue(val.identifier) - } - if(val.provider_phone){ - this.newOrganizationForm.get('phone').setValue(val.provider_phone) - } - if(val.provider_fax){ - this.newOrganizationForm.get('fax').setValue(val.provider_fax) - } - if(val.provider_address){ - let addressGroup = this.newOrganizationForm.get('address') - addressGroup.get('line1').setValue(val.provider_address.line1) - addressGroup.get('line2').setValue(val.provider_address.line2) - addressGroup.get('city').setValue(val.provider_address.city) - addressGroup.get('state').setValue(val.provider_address.state) - addressGroup.get('zip').setValue(val.provider_address.zip) - addressGroup.get('country').setValue(val.provider_address.country) - } - if(val.text) { - this.newOrganizationForm.get('name').setValue(val.text) - } - }); - - this.newOrganizationForm = new FormGroup({ - identifier: new FormControl([]), - name: new FormControl(null, Validators.required), - type: new FormControl(null, Validators.required), - phone: new FormControl(null, Validators.pattern('[- +()0-9]+')), - fax: new FormControl(null, Validators.pattern('[- +()0-9]+')), - email: new FormControl(null, Validators.email), - address: new FormGroup({ - line1: new FormControl(null), - line2: new FormControl(null), - city: new FormControl(null), - state: new FormControl(null), - zip: new FormControl(null), - country: new FormControl(null), - }) - }) - - } - - private resetAttachmentForm(){ - - this.newAttachmentForm = new FormGroup({ - name: new FormControl(null, Validators.required), - category: new FormControl(null, Validators.required), - file_type: new FormControl(null, Validators.required), - file_name: new FormControl(null, Validators.required), - file_content: new FormControl(null, Validators.required), - file_size: new FormControl(null), + //on close, we should redirect to the Medical history page. + modalRef.result.then((result) => { + this.router.navigate(['/medical-history']); + }).catch((error) => { + this.router.navigate(['/medical-history']); }) } - onAttachmentFileChange($event){ - console.log("onAttachmentFileChange") - let fileInput = $event.target as HTMLInputElement; - if (fileInput.files && fileInput.files[0]) { - let reader = new FileReader(); - reader.onloadend = () => { - // use a regex to remove data url part - const base64String = (reader.result as string).replace('data:', '').replace(/^.+,/, ''); - this.newAttachmentForm.get('file_content').setValue(base64String) - }; - reader.readAsDataURL(fileInput.files[0]); - this.newAttachmentForm.get('file_name').setValue(fileInput.files[0].name) - this.newAttachmentForm.get('file_size').setValue(fileInput.files[0].size) - } - } - - } diff --git a/frontend/src/app/pages/resource-creator/resource-creator.utilities.ts b/frontend/src/app/pages/resource-creator/resource-creator.utilities.ts deleted file mode 100644 index 10ec924e..00000000 --- a/frontend/src/app/pages/resource-creator/resource-creator.utilities.ts +++ /dev/null @@ -1,534 +0,0 @@ -import { - ResourceCreate, - ResourceCreateCondition, ResourceCreateAttachment, ResourceCreateMedication, - ResourceCreateOrganization, ResourceCreatePractitioner, - ResourceCreateProcedure -} from '../../models/fasten/resource_create'; -import { - Condition, - Medication, - Procedure, - Location as FhirLocation, - BundleEntry, - Bundle, - Organization, - Practitioner, MedicationRequest, Patient, Encounter, DocumentReference, Media, DiagnosticReport, Reference, Binary -} from 'fhir/r4'; -import {uuidV4} from '../../../lib/utils/uuid'; - -interface ResourceStorage { - [resourceType: string]: { - [resourceId: string]: Condition | Patient | MedicationRequest | Organization | FhirLocation | Practitioner | Procedure | Encounter | DocumentReference | Media | DiagnosticReport | Binary - } -} - - -export function GenerateR4Bundle(resourceCreate: ResourceCreate): Bundle { - let resourceStorage: ResourceStorage = {} //{"resourceType": {"resourceId": resourceData}} - resourceStorage = placeholderR4Patient(resourceStorage) - resourceStorage = resourceCreateConditionToR4Condition(resourceStorage, resourceCreate.condition) - - for(let attachment of resourceCreate.attachments) { - if(attachment.file_type == 'application/dicom' || - attachment.category.id == '18726-0' || //Radiology studies (set) - attachment.category.id == '27897-8' || // Neuromuscular electrophysiology studies (set) - attachment.category.id == '18748-4' // Diagnostic imaging study - ) { - //Diagnostic imaging study (DiagnosticReport -> Media) - resourceStorage = resourceAttachmentToR4DiagnosticReport(resourceStorage, attachment) - } - else { - resourceStorage = resourceAttachmentToR4DocumentReference(resourceStorage, attachment) - } - - } - for(let organization of resourceCreate.organizations) { - resourceStorage = resourceCreateOrganizationToR4Organization(resourceStorage, organization) - } - for(let practitioner of resourceCreate.practitioners) { - resourceStorage = resourceCreatePractitionerToR4Practitioner(resourceStorage, practitioner) - } - for(let medication of resourceCreate.medications) { - resourceStorage = resourceCreateMedicationToR4MedicationRequest(resourceStorage, medication) - } - for(let procedure of resourceCreate.procedures) { - resourceStorage = resourceCreateProcedureToR4Procedure(resourceStorage, procedure) - } - - - //DocumentReference -> (Optional) Binary - //DiagnosticReport -> Media - //ImagingStudy - //ImagingSelection - - console.log("POPULATED RESOURCE STORAGE", resourceStorage) - - let bundle = { - resourceType: 'Bundle', - type: 'transaction', - entry: [], - } as Bundle - for(let resourceType in resourceStorage) { - for(let resourceId in resourceStorage[resourceType]) { - let resource = resourceStorage[resourceType][resourceId] - bundle.entry.push({ - fullUrl: `urn:uuid:${resource.id}`, - resource: resource, - }) - } - } - - return bundle -} - -//Private methods - -function placeholderR4Patient(resourceStorage: ResourceStorage): ResourceStorage { - resourceStorage['Patient'] = resourceStorage['Patient'] || {} - let patientResource = { - resourceType: 'Patient', - id: uuidV4(), - name: [ - { - family: 'Placeholder', - given: ['Patient'], - } - ], - } as Patient - resourceStorage['Patient'][patientResource.id] = patientResource - return resourceStorage -} - -// this model is based on FHIR401 Resource Condition - http://hl7.org/fhir/R4/condition.html -function resourceCreateConditionToR4Condition(resourceStorage: ResourceStorage, resourceCreateCondition: ResourceCreateCondition): ResourceStorage { - resourceStorage['Condition'] = resourceStorage['Condition'] || {} - resourceStorage['Encounter'] = resourceStorage['Encounter'] || {} - - let note = [] - if (resourceCreateCondition.description) { - note.push({ - text: resourceCreateCondition.description, - }) - } - - let conditionResource = { - subject: { - reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient - }, - resourceType: 'Condition', - id: uuidV4(), - code: { - coding: resourceCreateCondition.data.identifier || [], - text: resourceCreateCondition.data.identifier[0].display, - }, - clinicalStatus: { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "code": resourceCreateCondition.status, - } - ] - }, - onsetDateTime: `${new Date(resourceCreateCondition.started.year, resourceCreateCondition.started.month-1,resourceCreateCondition.started.day).toISOString()}`, - abatementDateTime: resourceCreateCondition.stopped ? `${new Date(resourceCreateCondition.stopped.year,resourceCreateCondition.stopped.month-1, resourceCreateCondition.stopped.day).toISOString()}` : null, - recordedDate: new Date().toISOString(), - note: note - } as Condition - - - - resourceStorage['Condition'][conditionResource.id] = conditionResource - return resourceStorage -} - -// this model is based on FHIR401 Resource Procedure - http://hl7.org/fhir/R4/procedure.html -function resourceCreateProcedureToR4Procedure(resourceStorage: ResourceStorage, resourceCreateProcedure: ResourceCreateProcedure): ResourceStorage { - resourceStorage['Procedure'] = resourceStorage['Procedure'] || {} - - - let note = [] - if (resourceCreateProcedure.comment) { - note.push({ - text: resourceCreateProcedure.comment, - }) - } - - let encounterResource = { - resourceType: 'Encounter', - id: uuidV4(), - status: "finished", - subject: { - reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient - }, - participant: [ - { - individual: { - reference: `urn:uuid:${resourceCreateProcedure.performer}` //Practitioner - } - } - ], - period: { - start: `${new Date(resourceCreateProcedure.whendone.year, resourceCreateProcedure.whendone.month-1,resourceCreateProcedure.whendone.day).toISOString()}`, - end: `${new Date(resourceCreateProcedure.whendone.year, resourceCreateProcedure.whendone.month-1,resourceCreateProcedure.whendone.day).toISOString()}`, - }, - reasonReference: [ - { - reference: `urn:uuid:${findCondition(resourceStorage).id}` //Condition - } - ], - serviceProvider: { - reference: `urn:uuid:${resourceCreateProcedure.location}` //Organization - } - } as Encounter - resourceStorage['Encounter'][encounterResource.id] = encounterResource - - let procedureResource = { - subject: { - reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient - }, - status: "completed", - resourceType: 'Procedure', - - id: uuidV4(), - code: { - coding: resourceCreateProcedure.data.identifier || [], - text: resourceCreateProcedure.data.identifier[0].display, - }, - performedDateTime: `${new Date(resourceCreateProcedure.whendone.year, resourceCreateProcedure.whendone.month-1,resourceCreateProcedure.whendone.day).toISOString()}`, - encounter: { - reference: `urn:uuid:${encounterResource.id}` //Encounter - }, - reasonReference: [ - { - reference: `urn:uuid:${findCondition(resourceStorage).id}` //Condition - } - ], - report: (resourceCreateProcedure.attachments || []).map(attachmentId => { - return { - reference: `urn:uuid:${attachmentId}` //DocumentReference or DiagnosticReport - } - }), - performer: [ - { - actor: { - reference: `urn:uuid:${resourceCreateProcedure.performer}` //Practitioner - }, - onBehalfOf: { - reference: `urn:uuid:${resourceCreateProcedure.location}` //Organization - } - } - ], - note: note, - } as Procedure - resourceStorage['Procedure'][procedureResource.id] = procedureResource - - return resourceStorage -} - -// this model is based on FHIR401 Resource Organization - http://hl7.org/fhir/R4/organization.html -function resourceCreateOrganizationToR4Organization(resourceStorage: ResourceStorage, resourceCreateOrganization: ResourceCreateOrganization): ResourceStorage { - resourceStorage['Organization'] = resourceStorage['Organization'] || {} - - let telecom = [] - if (resourceCreateOrganization.phone) { - telecom.push({ - system: 'phone', - value: resourceCreateOrganization.phone, - }) - } - if (resourceCreateOrganization.fax) { - telecom.push({ - system: 'fax', - value: resourceCreateOrganization.fax, - }) - } - if (resourceCreateOrganization.email) { - telecom.push({ - system: 'email', - value: resourceCreateOrganization.email, - }) - } - - let organizationResource = { - resourceType: 'Organization', - id: resourceCreateOrganization.id, - name: resourceCreateOrganization.name, - identifier: resourceCreateOrganization.identifier || [], - type: [ - { - coding: resourceCreateOrganization.type.identifier || [], - } - ], - address: [ - { - line: [resourceCreateOrganization.address.line1, resourceCreateOrganization.address.line2], - city: resourceCreateOrganization.address.city, - state: resourceCreateOrganization.address.state, - postalCode: resourceCreateOrganization.address.zip, - country: resourceCreateOrganization.address.country, - } - ], - telecom: telecom, - active: true, - } as Organization - - resourceStorage['Organization'][organizationResource.id] = organizationResource - return resourceStorage - -} - -// this model is based on FHIR401 Resource Practitioner - http://hl7.org/fhir/R4/practitioner.html -function resourceCreatePractitionerToR4Practitioner(resourceStorage: ResourceStorage, resourceCreatePractitioner: ResourceCreatePractitioner): ResourceStorage { - resourceStorage['Practitioner'] = resourceStorage['Practitioner'] || {} - let telecom = [] - if (resourceCreatePractitioner.phone) { - telecom.push({ - system: 'phone', - value: resourceCreatePractitioner.phone, - }) - } - if (resourceCreatePractitioner.fax) { - telecom.push({ - system: 'fax', - value: resourceCreatePractitioner.fax, - }) - } - if (resourceCreatePractitioner.email) { - telecom.push({ - system: 'email', - value: resourceCreatePractitioner.email, - }) - } - let qualification = [] - if(resourceCreatePractitioner.profession){ - qualification.push({ - code: { - coding: resourceCreatePractitioner.profession.identifier || [], - } - }) - } - - resourceCreatePractitioner.name.split(" ") - - let practitionerResource = { - resourceType: 'Practitioner', - id: resourceCreatePractitioner.id, - name: [ - { - text: resourceCreatePractitioner.name, - }, - ], - identifier: resourceCreatePractitioner.identifier || [], - address: [ - { - line: [resourceCreatePractitioner.address.line1, resourceCreatePractitioner.address.line2], - city: resourceCreatePractitioner.address.city, - state: resourceCreatePractitioner.address.state, - postalCode: resourceCreatePractitioner.address.zip, - country: resourceCreatePractitioner.address.country, - } - ], - telecom: telecom, - active: true, - qualification: qualification - } as Practitioner - - resourceStorage['Practitioner'][practitionerResource.id] = practitionerResource - return resourceStorage -} - -// this model is based on FHIR401 Resource Medication - https://www.hl7.org/fhir/R4/MedicationRequest.html -function resourceCreateMedicationToR4MedicationRequest(resourceStorage: ResourceStorage, resourceCreateMedication: ResourceCreateMedication): ResourceStorage { - resourceStorage['MedicationRequest'] = resourceStorage['MedicationRequest'] || {} - - let encounterResource = { - resourceType: 'Encounter', - id: uuidV4(), - status: "finished", - subject: { - reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient - }, - participant: [ - { - individual: { - reference: `urn:uuid:${resourceCreateMedication.requester}` //Practitioner - } - } - ], - period: { - start: `${new Date(resourceCreateMedication.started.year, resourceCreateMedication.started.month-1,resourceCreateMedication.started.day).toISOString()}`, - end: resourceCreateMedication.stopped ? `${new Date(resourceCreateMedication.stopped.year, resourceCreateMedication.stopped.month-1,resourceCreateMedication.stopped.day).toISOString()}` : null, - }, - reasonReference: [ - { - reference: `urn:uuid:${findCondition(resourceStorage).id}` //Condition - } - ], - } as Encounter - resourceStorage['Encounter'][encounterResource.id] = encounterResource - - let medicationRequestResource = { - id: uuidV4(), - resourceType: 'MedicationRequest', - status: resourceCreateMedication.status, - statusReason: { - coding: resourceCreateMedication.whystopped.identifier || [], - }, - intent: 'order', - medicationCodeableConcept: { - coding: resourceCreateMedication.data.identifier || [], - }, - subject: { - reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient - }, - encounter: { - reference: `urn:uuid:${encounterResource.id}` //Encounter - }, - authoredOn: `${new Date(resourceCreateMedication.started.year,resourceCreateMedication.started.month-1,resourceCreateMedication.started.day).toISOString()}`, - requester: { - reference: `urn:uuid:${resourceCreateMedication.requester}` // Practitioner - }, - supportingInformation: (resourceCreateMedication.attachments || []).map((attachmentId) => { - return { - reference: `urn:uuid:${attachmentId}` //DocumentReference or DiagnosticReport - } - }), - reasonReference: [ - { - reference: `urn:uuid:${findCondition(resourceStorage).id}` //Condition - }, - ], - note: [ - { - text: resourceCreateMedication.instructions, - } - ], - dispenseRequest: { - validityPeriod: { - start: `${new Date(resourceCreateMedication.started.year,resourceCreateMedication.started.month-1,resourceCreateMedication.started.day).toISOString()}`, - end: resourceCreateMedication.stopped ? `${new Date(resourceCreateMedication.stopped.year,resourceCreateMedication.stopped.month-1,resourceCreateMedication.stopped.day).toISOString()}` : null, - }, - }, - } as MedicationRequest - resourceStorage['MedicationRequest'][medicationRequestResource.id] = medicationRequestResource - - return resourceStorage -} - -function resourceAttachmentToR4DocumentReference(resourceStorage: ResourceStorage, resourceAttachment: ResourceCreateAttachment): ResourceStorage { - resourceStorage['Binary'] = resourceStorage['Binary'] || {} - let binaryResource = { - id: uuidV4(), - resourceType: 'Binary', - contentType: resourceAttachment.file_type, - data: resourceAttachment.file_content, - } as Binary - resourceStorage['Binary'][binaryResource.id] = binaryResource - - - resourceStorage['DocumentReference'] = resourceStorage['DocumentReference'] || {} - - let documentReferenceResource = { - id: resourceAttachment.id, - resourceType: 'DocumentReference', - status: 'current', - category: [ - { - coding: resourceAttachment.category.identifier || [], - text: resourceAttachment.category.text, - } - ], - // description: resourceAttachment.description, - subject: { - reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient - }, - content: [ - { - attachment: { - contentType: resourceAttachment.file_type, - url: `urn:uuid:${binaryResource.id}`, //Binary - title: resourceAttachment.name, - } - } - ], - context: [ - { - related: [ - { - reference: `urn:uuid:${findCondition(resourceStorage).id}` //Condition - } - ] - } - ] - // date: `${new Date(resourceDocumentReference.date.year,resourceDocumentReference.date.month-1,resourceDocumentReference.date.day).toISOString()}`, - } as DocumentReference - resourceStorage['DocumentReference'][documentReferenceResource.id] = documentReferenceResource - - return resourceStorage -} - -function resourceAttachmentToR4DiagnosticReport(resourceStorage: ResourceStorage, resourceAttachment: ResourceCreateAttachment): ResourceStorage { - resourceStorage['Binary'] = resourceStorage['Binary'] || {} - let binaryResource = { - id: uuidV4(), - resourceType: 'Binary', - contentType: resourceAttachment.file_type, - data: resourceAttachment.file_content, - } as Binary - resourceStorage['Binary'][binaryResource.id] = binaryResource - - resourceStorage['Media'] = resourceStorage['Media'] || {} - - let mediaResource = { - id: uuidV4(), - resourceType: 'Media', - status: 'completed', - type: { - coding: resourceAttachment.category.identifier || [], - display: resourceAttachment.category.text, - }, - subject: { - reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient - }, - content: { - contentType: resourceAttachment.file_type, - url: `urn:uuid:${binaryResource.id}`, //Binary, - title: resourceAttachment.name, - }, - } as Media - resourceStorage['Media'][mediaResource.id] = mediaResource - - resourceStorage['DiagnosticReport'] = resourceStorage['DiagnosticReport'] || {} - let diagnosticReportResource = { - id: resourceAttachment.id, - resourceType: 'DiagnosticReport', - status: 'final', - code: { - coding: resourceAttachment.category.identifier || [], - }, - subject: { - reference: `urn:uuid:${findPatient(resourceStorage).id}` //Patient - }, - media: [ - { - link: { - reference: `urn:uuid:${mediaResource.id}` //Media - } - }, - ], - } as DiagnosticReport - resourceStorage['DiagnosticReport'][diagnosticReportResource.id] = diagnosticReportResource - - return resourceStorage -} - - -function findCondition(resourceStorage: ResourceStorage): Condition { - let [conditionId] = Object.keys(resourceStorage['Condition']) - return resourceStorage['Condition'][conditionId] as Condition -} - -function findPatient(resourceStorage: ResourceStorage): Patient { - let [patientId] = Object.keys(resourceStorage['Patient']) - return resourceStorage['Patient'][patientId] as Patient -} diff --git a/frontend/src/app/pages/resource-detail/resource-detail.component.html b/frontend/src/app/pages/resource-detail/resource-detail.component.html index 27306108..92f813fc 100644 --- a/frontend/src/app/pages/resource-detail/resource-detail.component.html +++ b/frontend/src/app/pages/resource-detail/resource-detail.component.html @@ -10,7 +10,7 @@ - +

    An error occurred while parsing FHIR resource

    diff --git a/frontend/src/app/pages/source-detail/source-detail.component.html b/frontend/src/app/pages/source-detail/source-detail.component.html index 2ed01a99..958be225 100644 --- a/frontend/src/app/pages/source-detail/source-detail.component.html +++ b/frontend/src/app/pages/source-detail/source-detail.component.html @@ -1,6 +1,6 @@
    -
    +
    @@ -37,9 +37,9 @@
    Address:
    {{getPatientAddress()}}
    ID:
    -
    {{selectedPatient?.source_resource_id}}
    +
    {{selectedPatient?.source_resource_id}}
    MRN:
    -
    {{getPatientMRN()}}
    +
    {{getPatientMRN()}}
    @@ -63,11 +63,11 @@
    - + >
    diff --git a/frontend/src/app/pages/source-detail/source-detail.component.ts b/frontend/src/app/pages/source-detail/source-detail.component.ts index 7db16301..e59934cb 100644 --- a/frontend/src/app/pages/source-detail/source-detail.component.ts +++ b/frontend/src/app/pages/source-detail/source-detail.component.ts @@ -3,7 +3,7 @@ import {ActivatedRoute, Router} from '@angular/router'; import {Source} from '../../models/fasten/source'; import {FastenApiService} from '../../services/fasten-api.service'; import {ResourceFhir} from '../../models/fasten/resource_fhir'; -import {getPath} from '../../components/list-generic-resource/utils'; +import {getPath} from '../../components/fhir-datatable/datatable-generic-resource/utils'; @Component({ selector: 'app-source-detail', diff --git a/frontend/src/app/pipes/human-name.pipe.spec.ts b/frontend/src/app/pipes/human-name.pipe.spec.ts new file mode 100644 index 00000000..bd0cf04c --- /dev/null +++ b/frontend/src/app/pipes/human-name.pipe.spec.ts @@ -0,0 +1,8 @@ +import { HumanNamePipe } from './human-name.pipe'; + +describe('HumanNamePipe', () => { + it('create an instance', () => { + const pipe = new HumanNamePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/pipes/human-name.pipe.ts b/frontend/src/app/pipes/human-name.pipe.ts new file mode 100644 index 00000000..799dea2d --- /dev/null +++ b/frontend/src/app/pipes/human-name.pipe.ts @@ -0,0 +1,36 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import {evaluate} from 'fhirpath'; +import {HumanNameModel} from '../../lib/models/datatypes/human-name-model'; + +@Pipe({ + name: 'humanName' +}) +export class HumanNamePipe implements PipeTransform { + + + + transform(humanNameModel: HumanNameModel | HumanNameModel[], ...args: unknown[]): unknown { + if(!humanNameModel){ + return null + } + + let names = [] + + if(Array.isArray(humanNameModel)){ + //array of values + for(let humanName of humanNameModel){ + names.push(this.getDisplayName(humanName)); + } + } else { + //single value + names.push(this.getDisplayName(humanNameModel)); + } + + return names.join(", ") + } + + getDisplayName(humanNameModel: HumanNameModel): string { + return humanNameModel.textName ? humanNameModel.textName : `${humanNameModel.givenName} ${humanNameModel.familyName}`.trim(); + } + +} diff --git a/frontend/src/app/pipes/pipes.module.ts b/frontend/src/app/pipes/pipes.module.ts index 7949cc7d..f95f1441 100644 --- a/frontend/src/app/pipes/pipes.module.ts +++ b/frontend/src/app/pipes/pipes.module.ts @@ -6,6 +6,8 @@ import {FhirPathPipe} from './fhir-path.pipe'; import {FilterPipe} from './filter.pipe'; import { ShortDomainPipe } from './short-domain.pipe'; import { DatasetLatestEntryPipe } from './dataset-latest-entry.pipe'; +import { HumanNamePipe } from './human-name.pipe'; +import { ReferenceUriPipe } from './reference-uri.pipe'; @NgModule({ declarations: [ @@ -14,6 +16,8 @@ import { DatasetLatestEntryPipe } from './dataset-latest-entry.pipe'; FilterPipe, ShortDomainPipe, DatasetLatestEntryPipe, + HumanNamePipe, + ReferenceUriPipe, ], imports: [ @@ -22,7 +26,9 @@ import { DatasetLatestEntryPipe } from './dataset-latest-entry.pipe'; FhirPathPipe, FilterPipe, ShortDomainPipe, - DatasetLatestEntryPipe + DatasetLatestEntryPipe, + HumanNamePipe, + ReferenceUriPipe ] }) export class PipesModule {} diff --git a/frontend/src/app/pipes/reference-uri.pipe.spec.ts b/frontend/src/app/pipes/reference-uri.pipe.spec.ts new file mode 100644 index 00000000..126d9d56 --- /dev/null +++ b/frontend/src/app/pipes/reference-uri.pipe.spec.ts @@ -0,0 +1,8 @@ +import { ReferenceUriPipe } from './reference-uri.pipe'; + +describe('ReferenceUriPipe', () => { + it('create an instance', () => { + const pipe = new ReferenceUriPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/pipes/reference-uri.pipe.ts b/frontend/src/app/pipes/reference-uri.pipe.ts new file mode 100644 index 00000000..a2181d61 --- /dev/null +++ b/frontend/src/app/pipes/reference-uri.pipe.ts @@ -0,0 +1,18 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import {generateReferenceUriFromResourceOrReference} from '../../lib/utils/bundle_references'; +import {FastenDisplayModel} from '../../lib/models/fasten/fasten-display-model'; +import {Reference, Resource} from 'fhir/r4'; + +@Pipe({ + name: 'referenceUri' +}) +export class ReferenceUriPipe implements PipeTransform { + + transform(value: FastenDisplayModel | Resource | Reference, ...args: unknown[]): unknown { + if (!value) { + return value + } + return generateReferenceUriFromResourceOrReference(value) + } + +} diff --git a/frontend/src/app/services/fasten-api.service.ts b/frontend/src/app/services/fasten-api.service.ts index 2867358a..b331faa3 100644 --- a/frontend/src/app/services/fasten-api.service.ts +++ b/frontend/src/app/services/fasten-api.service.ts @@ -26,7 +26,9 @@ import {ResourceGraphResponse} from '../models/fasten/resource-graph-response'; import { fetchEventSource } from '@microsoft/fetch-event-source'; import {BackgroundJob} from '../models/fasten/background-job'; import {SupportRequest} from '../models/fasten/support-request'; - +import { + List +} from 'fhir/r4'; @Injectable({ providedIn: 'root' }) @@ -107,6 +109,25 @@ export class FastenApiService { ); } + createRelatedResourcesFastenSource(resourceList: List): Observable { + + console.log(resourceList) + let bundleBlob = new Blob([JSON.stringify(resourceList)], { type: 'application/json' }); + let bundleFile = new File([ bundleBlob ], 'related.json', { type: 'application/json' }); + + const formData = new FormData(); + formData.append('file', bundleFile); + + return this._httpClient.post(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/resource/related`, formData) + .pipe( + map((response: ResponseWrapper) => { + console.log("RELATED RESOURCES RESPONSE", response) + return response.data as Source + }) + ); + } + + getSources(): Observable { return this._httpClient.get(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/source`) .pipe( @@ -190,6 +211,10 @@ export class FastenApiService { return this._httpClient.post(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/query`, query) } + // requires: + // - source_id: string + // - source_resource_type: string + // - source_resource_id: string getResourceGraph(graphType?: string, selectedResourceIds?: Partial[]): Observable { if(!graphType){ graphType = "MedicalHistory" @@ -228,6 +253,7 @@ export class FastenApiService { } //this method allows a user to manually group related FHIR resources together (conditions, encounters, etc). + // @deprecated - replaced by Create Manual Record Wizard createResourceComposition(title: string, resources: ResourceFhir[]){ return this._httpClient.post(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/resource/composition`, { "resources": resources, diff --git a/frontend/src/app/services/nlm-clinical-table-search.service.ts b/frontend/src/app/services/nlm-clinical-table-search.service.ts index 72159677..e25a4196 100644 --- a/frontend/src/app/services/nlm-clinical-table-search.service.ts +++ b/frontend/src/app/services/nlm-clinical-table-search.service.ts @@ -1486,4 +1486,97 @@ export class NlmClinicalTableSearchService { // return of(result) } + //https://hl7.org/fhir/r4/v3/ActEncounterCode/vs.html + //manually created for patient understanding + searchEncounterClassification(searchTerm: string): Observable { + + let searchOptions: NlmSearchResults[] = [ + { + id: "AMB", + identifier: [{ + code: "AMB", + display: "ambulatory", + system: "http://terminology.hl7.org/CodeSystem/v3-ActCode" + }], + text: "Routine Check-up or Preventive Visit" + }, + { + id: "EMER", + identifier: [{ + code: "EMER", + display: "emergency", + system: "http://terminology.hl7.org/CodeSystem/v3-ActCode" + }], + text: "Emergency Room (ER) Visit" + }, + { + id: "HH", + identifier: [{ + code: "HH", + display: "home health", + system: "http://terminology.hl7.org/CodeSystem/v3-ActCode" + }], + text: "Home Health" + }, + { + id: "IMP", + identifier: [{ + code: "IMP", + display: "inpatient encounter", + system: "http://terminology.hl7.org/CodeSystem/v3-ActCode" + }], + text: "Hospital Admission involving overnight stay, room, board, and ongoing nursing care." + }, + { + id: "SS", + identifier: [{ + code: "SS", + display: "short stay", + system: "http://terminology.hl7.org/CodeSystem/v3-ActCode" + }], + text: "Brief Hospital Admission for a predetermined period, typically less than 24 hours." + }, + { + id: "VR", + identifier: [{ + code: "VR", + display: "virtual", + system: "http://terminology.hl7.org/CodeSystem/v3-ActCode" + }], + text: "Virtual/Telemedicine Appointment" + } + ] + let result = searchTerm.length == 0 ? searchOptions : searchOptions.filter((v) => v['text'].toLowerCase().indexOf(searchTerm.toLowerCase()) > -1).slice(0, 10) + return of(result) + } + + //https://hl7.org/fhir/r4/codesystem-service-type.html + searchEncounterServiceType(searchTerm: string): Observable { + + //https://tx.fhir.org/r4/ValueSet/$expand?_format=json&filter=Referral&url=http://hl7.org/fhir/ValueSet/service-type&incomplete-ok=true + let queryParams = { + '_format': 'json', + 'filter':searchTerm, + 'url': 'http://hl7.org/fhir/ValueSet/service-type', + 'incomplete-ok': 'true' + } + + + // `https://ontology.nhs.uk/production1/fhir/` + // return this._httpClient.get(`https://tx.fhir.org/r4/ValueSet/$expand`, {params: queryParams}) + return this._httpClient.get(`https://ontology.nhs.uk/production1/fhir/ValueSet/$expand`, {params: queryParams}) + .pipe( + map((response) => { + + return (response.expansion.contains || []).map((valueSetItem):NlmSearchResults => { + return { + id: valueSetItem.code, + identifier: [valueSetItem], + text: valueSetItem.display, + } + }) + }) + ) + } + } diff --git a/frontend/src/assets/scss/custom/_modal.scss b/frontend/src/assets/scss/custom/_modal.scss index c8ea6e59..34c7fa32 100755 --- a/frontend/src/assets/scss/custom/_modal.scss +++ b/frontend/src/assets/scss/custom/_modal.scss @@ -6,7 +6,7 @@ position: fixed; top: 0; left: 0; - z-index: 1055; + z-index: 1050; display: none; width: 100%; height: 100%; diff --git a/frontend/src/assets/scss/custom/_nav.scss b/frontend/src/assets/scss/custom/_nav.scss index ea93fcd2..fdf5ef1c 100755 --- a/frontend/src/assets/scss/custom/_nav.scss +++ b/frontend/src/assets/scss/custom/_nav.scss @@ -1,269 +1,269 @@ /* ###### 4.8 Nav ###### */ - -.az-nav { - @include media-breakpoint-up(md) { align-items: center; } - - .nav-link { - display: block; - color: $gray-700; - padding: 0; - position: relative; - line-height: normal; - - @include hover-focus() { - color: $gray-900; - } - - + .nav-link { - padding-top: 12px; - margin-top: 12px; - border-top: 1px dotted $gray-500; - - @include media-breakpoint-up(md) { - padding-top: 0; - margin-top: 0; - border-top: 0; - padding-left: 15px; - margin-left: 15px; - border-left: 1px dotted $gray-500; - } - } - - &.active { color: $az-color-primary; } +@media (min-width: 768px) { + .az-nav { + align-items: center; } } +.az-nav .nav-link { + display: block; + color: #596882; + padding: 0; + position: relative; + line-height: normal; +} +.az-nav .nav-link:hover-focus() { + color: #1c273c; +} +.az-nav .nav-link + .nav-link { + padding-top: 12px; + margin-top: 12px; + border-top: 1px dotted #97a3b9; +} +@media (min-width: 768px) { + .az-nav .nav-link + .nav-link { + padding-top: 0; + margin-top: 0; + border-top: 0; + padding-left: 15px; + margin-left: 15px; + border-left: 1px dotted #97a3b9; + } +} +.az-nav .nav-link.active { + color: #5b47fb; +} .az-nav-column { flex-direction: column; - - .nav-link { - padding: 0; - height: $az-height-base; - color: $gray-900; - display: flex; - align-items: center; - justify-content: flex-start; - - i { - font-size: 24px; - line-height: 0; - width: 24px; - margin-right: 12px; - text-align: center; - @include transition($transition-base); - - &:not([class*=' tx-']) { color: $gray-600; } - - &.typcn { line-height: .9; } - } - - span { - font-weight: 400; - font-size: 11px; - color: $gray-500; - margin-left: auto; - } - - &:hover, - &:focus { - color: $gray-900; - i:not([class*=' tx-']) { color: $gray-900; } - } - - &.active { - position: relative; - - &::before { - content: ''; - position: absolute; - top: 6px; - bottom: 6px; - left: -28px; - width: 3px; - background-color: $indigo; - @include border-radius(); - display: none; - } - - &, - &:hover, - &:focus { - color: $indigo; - i { color: $indigo; } - } - } - - + .nav-link { border-top: 1px dotted $gray-400; } - } - - &.sm { - .nav-link { - font-size: $font-size-base; - font-weight: 400; - padding: 10px 0; - - i { font-size: 21px; } - } +} +.az-nav-column .nav-link { + padding: 0; + height: 38px; + color: #1c273c; + display: flex; + align-items: center; + justify-content: flex-start; +} +.az-nav-column .nav-link i { + font-size: 24px; + line-height: 0; + width: 24px; + margin-right: 12px; + text-align: center; + transition: all 0.2s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .az-nav-column .nav-link i { + transition: none; } } - -.az-nav-dark { - .nav-link { - color: rgba(#fff, .7); - @include hover-focus() { color: #fff; } - - + .nav-link { border-color: $gray-700; } - &.active { color: $az-color-primary; } - } +.az-nav-column .nav-link i:not([class*=" tx-"]) { + color: #7987a1; +} +.az-nav-column .nav-link i.typcn { + line-height: 0.9; +} +.az-nav-column .nav-link span { + font-weight: 400; + font-size: 11px; + color: #97a3b9; + margin-left: auto; +} +.az-nav-column .nav-link:hover, .az-nav-column .nav-link:focus { + color: #1c273c; +} +.az-nav-column .nav-link:hover i:not([class*=" tx-"]), .az-nav-column .nav-link:focus i:not([class*=" tx-"]) { + color: #1c273c; +} +.az-nav-column .nav-link.active { + position: relative; +} +.az-nav-column .nav-link.active::before { + content: ""; + position: absolute; + top: 6px; + bottom: 6px; + left: -28px; + width: 3px; + background-color: #5b47fb; + border-radius: 3px; + display: none; +} +.az-nav-column .nav-link.active, .az-nav-column .nav-link.active:hover, .az-nav-column .nav-link.active:focus { + color: #5b47fb; +} +.az-nav-column .nav-link.active i, .az-nav-column .nav-link.active:hover i, .az-nav-column .nav-link.active:focus i { + color: #5b47fb; +} +.az-nav-column .nav-link + .nav-link { + border-top: 1px dotted #b4bdce; +} +.az-nav-column.sm .nav-link { + font-size: 0.875rem; + font-weight: 400; + padding: 10px 0; +} +.az-nav-column.sm .nav-link i { + font-size: 21px; } -.az-nav-colored-bg { - .nav-link { - + .nav-link { border-color: rgba(#fff, .4); } - &.active { color: #fff; } - } +.az-nav-dark .nav-link { + color: rgba(255, 255, 255, 0.7); +} +.az-nav-dark .nav-link:hover-focus() { + color: #fff; +} +.az-nav-dark .nav-link + .nav-link { + border-color: #596882; +} +.az-nav-dark .nav-link.active { + color: #5b47fb; +} + +.az-nav-colored-bg .nav-link + .nav-link { + border-color: rgba(255, 255, 255, 0.4); +} +.az-nav-colored-bg .nav-link.active { + color: #fff; } .az-nav-line { position: relative; - - .nav-link { - padding: 0; - color: $gray-700; - position: relative; - - @include hover-focus() { color: $gray-900; } - - + .nav-link { - margin-top: 15px; - - @include media-breakpoint-up(md) { - margin-top: 0; - margin-left: 30px; - } - } - - &.active { - color: $gray-900; - - &::before { - content: ''; - position: absolute; - top: 0; - bottom: 0; - left: -20px; - width: 2px; - background-color: $gray-900; - - @include media-breakpoint-up(md) { - top: auto; - bottom: -20px; - left: 0; - right: 0; - height: 2px; - width: auto; - } - } - } +} +.az-nav-line .nav-link { + padding: 0; + color: #596882; + position: relative; +} +.az-nav-line .nav-link:hover-focus() { + color: #1c273c; +} +.az-nav-line .nav-link + .nav-link { + margin-top: 15px; +} +@media (min-width: 768px) { + .az-nav-line .nav-link + .nav-link { + margin-top: 0; + margin-left: 30px; } - - &.az-nav-dark { - .nav-link { - color: rgba(#fff, .7); - @include hover-focus() { color: #fff; } - - &.active { - color: #fff; - &::before { background-color: #fff; } - } - } +} +.az-nav-line .nav-link.active { + color: #1c273c; +} +.az-nav-line .nav-link.active::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: -20px; + width: 2px; + background-color: #1c273c; +} +@media (min-width: 768px) { + .az-nav-line .nav-link.active::before { + top: auto; + bottom: -20px; + left: 0; + right: 0; + height: 2px; + width: auto; } } +.az-nav-line.az-nav-dark .nav-link { + color: rgba(255, 255, 255, 0.7); +} +.az-nav-line.az-nav-dark .nav-link:hover-focus() { + color: #fff; +} +.az-nav-line.az-nav-dark .nav-link.active { + color: #fff; +} +.az-nav-line.az-nav-dark .nav-link.active::before { + background-color: #fff; +} .az-nav-tabs { padding: 15px 15px 0; - background-color: $gray-300; - - .lSSlideOuter { - position: relative; - padding-left: 32px; - padding-right: 35px; - } - - .lSSlideWrapper { overflow: visible; } - - .lSAction { - > a { - display: block; - height: 40px; - top: 16px; - opacity: 1; - background-color: $gray-400; - background-image: none; - - @include hover-focus() { background-color: darken($gray-400, 5%); } - - &::before { - font-family: 'Ionicons'; - font-size: 18px; - position: absolute; - top: -4px; - left: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - } - - &.lSPrev { - left: -32px; - &::before { content: '\f3cf'; } - } - - &.lSNext { - right: -35px; - &::before { content: '\f3d1'; } - } - - &.disabled { - background-color: $gray-200; - color: #fff; - } - } - } - - .lightSlider { display: flex; } - - .tab-item { - flex-shrink: 0; - display: block; - float: none; - min-width: 150px; - } - - .tab-link { - display: flex; - align-items: center; - justify-content: center; - padding: 10px 20px; - line-height: 1.428; - color: $gray-700; - white-space: nowrap; - background-color: $gray-200; - - @include hover-focus() { background-color: $gray-100; } - - &.active { - background-color: #fff; - color: $gray-900; - font-weight: 500; - } - } + background-color: #cdd4e0; +} +.az-nav-tabs .lSSlideOuter { + position: relative; + padding-left: 32px; + padding-right: 35px; +} +.az-nav-tabs .lSSlideWrapper { + overflow: visible; +} +.az-nav-tabs .lSAction > a { + display: block; + height: 40px; + top: 16px; + opacity: 1; + background-color: #b4bdce; + background-image: none; +} +.az-nav-tabs .lSAction > a:hover-focus() { + background-color: #a5afc4; +} +.az-nav-tabs .lSAction > a::before { + font-family: "Ionicons"; + font-size: 18px; + position: absolute; + top: -4px; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; +} +.az-nav-tabs .lSAction > a.lSPrev { + left: -32px; +} +.az-nav-tabs .lSAction > a.lSPrev::before { + content: "\f3cf"; +} +.az-nav-tabs .lSAction > a.lSNext { + right: -35px; +} +.az-nav-tabs .lSAction > a.lSNext::before { + content: "\f3d1"; +} +.az-nav-tabs .lSAction > a.disabled { + background-color: #e3e7ed; + color: #fff; +} +.az-nav-tabs .lightSlider { + display: flex; +} +.az-nav-tabs .tab-item { + flex-shrink: 0; + display: block; + float: none; + min-width: 150px; +} +.az-nav-tabs .tab-link { + display: flex; + align-items: center; + justify-content: center; + padding: 10px 20px; + line-height: 1.428; + color: #596882; + white-space: nowrap; + background-color: #e3e7ed; +} +.az-nav-tabs .tab-link:hover-focus() { + background-color: #f4f5f8; +} +.az-nav-tabs .tab-link.active { + background-color: #fff; + color: #1c273c; + font-weight: 500; } .az-tab-pane { display: none; - - &.active { display: block; } +} +.az-tab-pane.active { + display: block; } diff --git a/frontend/src/assets/sources/fasten.png b/frontend/src/assets/sources/fasten.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b23668997676e8ca78bd0a94e184a359634504 GIT binary patch literal 7083 zcmeHM`8!nY|JNc#XnES~OVTG>;h9L5B1UEBnPhvC(qKq341-EBDa%+ww#wK;GRR(0 zNT{(4r;w0!EMuKHbI$o5J)eK!^UL>o&UKyZyx*_;^?tqX`@Zh`p8LAyp{2Qru%MVA zA0MBvDb~oEk8d-u@owLOB3j3{ZbCns{H#sRZW?G)4nvb2H?j79e0)NCH{MNrPty;g zbgJb=+j9tlEU}UCpGbc<+|~rwmP4gEP<{rKodRVgK&dgjCl7hl`@F|tyobSH_-*ix z4=>mgBwh#oTtR{p=z#;TS#z(La~(`TPkV4?5*h4=yV~HlZ{XMYP!fd~82|>lgAjyF zj3J$^aK%d~J&qTCo9A%}oSjBGTHq2I6myq1_5*3IgK4QyL;!f*mdoWLOa}Zol()2q zkiB?QzmWGe@aK!aRabXzw-VX=j!T9^U zP#<1l7F1sW-LT`fyoH_4b6>uIcswK_g154a+;#4aC75O-T{CKVd~hE$b6S1q}@Y0%s(GWr9VokEN4nKBm9jCH&nu3KEX|Oa7`IhSq#0(g_`T( zU!%yv98z8gB}MY;=+N70xbPX27|CmW2fJBwKQ+UQ&v5Evo{uwl_ZE-6fUK`0KZg*{ z%V6~@=n54-ud9`>lC>@}xo-Lg74RnkS-mXWvL?;G+kp%mc!nUVoK*K+E4V>lEk&rWZ~i{2rwrmUMcG zM{Kk7(B1Jre*~v$Z5rV%q4Xuy+PS{P(ZRL;rL1jpf8LmX2ktCL4YA4EwY_Q>7Ld@o zog5w^EbZt?p<8VdkiIoZX)=!S`0gi~Z7nS}**jwLSW4q4uG_J~ifAI`zkDL2!`2Kq zT6{HDJcztx=kspwp2UuRxHjGLr3b@!Dm{?lZKhjxgpfGUmX|K}pP7!pl4$FL0l<`@ z*u+x-9D#N+^u3gCIZAljta0kd2hlV-wSVkC?#F755Dxn<^kh*sf}XXP<^ER^FxJ|6 zh6Dc52avdr7G118$sKm$J1*+@)SU8SL^L)`xI9D4M_BJ4sjhBE1J956VDP>faMQmn z#~H1bFsuEn=H<)_Pdl_FJ<}~Lwg^L>yl^fJ<&1_ z#@5Rcj!F!ui018PM7;ejK0c^v1W@L4No+4Md4kbN{dalg0nH8=3gPE*lBVn%5BbWp z@s6#s1fx|AlKK^_xNnARBfqB%!`Rj&cU{cw2*dcI&}zKE-YEL+?z1GdcUbY=D)AG0 zV(7cORH{Dt?Jxl-?g1nf?FH|{FH#j^6+}w_$~E5KBxO<@#{@Q;6 z+nV97nSJ=|XNU4WiQ@&)Ab9MdW+dI}OFi4@sUdLmy7!^sl7Yu`tIyUXSu;7pA2#<~ zirYR`d0DoGunnGmuoHifI4dHo8;21bFpMbjtmlsT({I6D}r^LI3(lV2q@>`Xfcje7ef*@?3cWCTKD4bgZ-P`c0(G zENg=?&OSXIH1@T{``B$F!#L#XxPMQX>(r|5ifs))`*F{VHm9ZaOr$q;u$5$aPpMV@3?gAn%4fX+qv3$*Z*-1*+@r#L zYNM?5Q&WDgbAmbQwyd@RKVWR=nkPkC6$!^}z?!B~#>JJ`Q>a|~PSSz2&q*;T%D@es zE3W)YXETbya8X(CmpB&s(-_C2`F6uT=FiRI&QWBd$G3D zqLpXHoO(Qb@FwW+76VHlhu`4I*4q5upNM)l2G#qukqq^0II=729$jW>{pNt}DEtDJ z17lE5-v;N`jo&I*aL;do6u$I#(NF5C>wkfnIk855yu1)zK!$Zpj>SiFI!BYdLocGx zx5N?mQHgwv5;=y50bpkYY{J$?VRiWMxZS_W9(U=Y38D)z#5;t+itB^(bEzvJZU}1n z@^wBsFyK9CBkHua$`w5w`$Y_^b4QQGd6WnHjzqRUDJ=;L_$su`Wo>4(E3JMcvuZ6t zI!_^v$ae*(S_UUAq+Nzmmkf$rbb{We`_|Z0Y-NbZmE*+NQuml7&P&+Gq`xzE3#(FNEH}2AlRs`GV7W*@g>#{jDs!lbN?zw#;>bOjl=j z{lUf$NTpXfUCFmsk2C7cIZ+kpOJ4_rOe^exoJJ3qz*rp9L04;e!Bs$2jS=$_3kbzb zzA+4a%kqyV=lq$KO1B5Dg`Q=9jx1r$WU78^oU%#ArR6vQMa1DpddbqoCtb*=%BsVY z58zGuYyrPheV;`8TI-U3ofNmyMm?^P&$7pKl0RAOqXnNI-mH*hjutl*t*)2%l>R;O z1+o~6$FFJ=Q0|VFWKVbh;Af?)Jd)@hjc5APCw@?gbty7*%2&y4z^l! zCw2zN-%oNzm0lFBf@OMz$6mR5Jg(d$&uZP=t;e?Gmp;ok*QU~^I?~9?CF@W?bgxMa@ zZ@A2S&WAUtGz4&A@?jU%_eH;{__&sCf9% zg}|=z8*`y=6*^t9I7%xv?M#(pix_LF@*B>{QP=X5HqQM&0 zDiA&f5hPHFEPeS#ulfKF;_?HBY%`B?x>E_D5h@p!sQzZ4l#z?hWf~(>E-da=;CHp| z>u9P0D#z;^o4%Z$VB+_pf~k>JtL>q>{!h5q;xQK-5ce^Mrg2d#sdngWM=V{wXfFEp z)2|~q4{ySgN_dB98Yj)AkVM5q9c0(I6lqg2g7%@WTwZeGFj3mWYJFs^IKK+J=+6TT z<1-lw&^|qevDE~3{HXx?^L;B+G=*-}UyxIGKn9=v288YjqnC?kkE;gp<@A*-S-gdN z%-RKvZCXx;ox_-5qnjZW<9f-VA=Wzq^J4u=>3{((TB~I35oRY|KBgUdA#4ax+zMSP z&S1r<S0-WhZx41<27Chn4beb4tQUsMF$$I zEGVH@EMuc+>?`xIsrU)#quQV`ww4aFr&A);PE9wgxIi~~2lh*s zjnvx2RF^b2R@N8|d&w|Vv&Kamm!}l`f+6?X@o&Q&>8~efJ&GNcnJ#ZqHPpWwM0V`_ z?3)?%u}`{c7C*nld_I%86Ja`V7*pXk(xhO8`efVrJ}u?z(Zu`eVXU>7_95-p52;6x z;1aQZrp!7N=E18$Dz%`1cO2lVg~idy=r(balzziwb?4@yU~g^{bP}F5zl7 zx1V3VcM0cqqu@j#wCMEygnq5+lvk{vNjF;FeycZ=X`wT5FL~Zv%PEhjwTThq#${Zs zxa$47J7N^Oid&sKlMPj#cjF5$;IAb%}A!E1D{C zM6SNPiAtaOn`kF%vucKK>%q%1tT7yGyuqipo{@C@b54oPeiPsUs{{dk9kOybUwY&_ zukP>Nj8_aj8~?+#GFPLG*2T5BY$Qa>SS}n)8vAuVLE7}h4CRk(OesIZJg65o(s9u3 zUOj(&=9OkSrU=CXdo$_gI+sJ3LF58|ouUR0a>*W9*5;Zurg>jlX~RO++Lzm-n7i~e zCR9OuwT-7Qv~J8GEy=NRos!mV_Kj{MS&**-cl}TxBUiGhjJdWbMGBdwE}@gNxBjg9 zIUPI7Xq~%MT+q+c=x{V~EGFIX0f|YP47V0d7r>u%{}RZuyeq=a2%7I%`gH~{t6t9o zQqMX~@(wYbz3VTXq75c8zMD-o%a-^hvm%Rw4S^1=b2?U18cqVG3>*PlyTd(sN^;^_ zW&+|qjrWsb9r^rKB(aPBNY@1S-2hADM~^!v@Hp! zp^nEv}eyWEA=ax*n-g=j2d?~t92iIsU6w)29n`%#7n zu279Rd*gN2{#@zwpf3hA$yqwtE zMFc0!5Fx%HQnd6I`i*eEn^U65x89GNv^8ARy>^fxVna>lcbt>AqXqz`I-iw*L$0Ym z${u|v$lGZMTsd~Do2DRDi(PG6=EM=>yjj~9zmL3n97Wc7jXDRm|7xs|Z}ux~Qja;W z>z1+~`)#z)DXGhBS$p`(A)g#6^__SDl8esS(*mcIN9%hGBV<`JOivTagtSQerKdry z$7zyAI%kZQB%k;?Ov^UK5h-VY9j*YbF*>G4110Zj@l>SXEg(#2YS2fN{4 optgroup > .divider { z-index: 1000; } +// text overflow add ellipsis +.text-overflow-ellipsis { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + // Form Validation .form-control.ng-invalid.ng-touched { @@ -245,3 +252,51 @@ app-medical-sources-filter > .az-content-left-components:hover{ -o-transition: width 0.2s ease-in-out; transition: width 0.2s ease-in-out; } + +//Fhir Datadable +.datatable-body .datatable-body-row.active { + background-color: $indigo !important; + color: #fff; +} + +// Callouts + + +.bd-callout { + padding: 1.25rem; + margin-top: 1.25rem; + margin-bottom: 1.25rem; + border: 1px solid #eee; + border-left-width: .25rem; + border-radius: .25rem; +} + +.bd-callout h4 { + margin-top: 0; + margin-bottom: .25rem; +} + +.bd-callout p:last-child { + margin-bottom: 0; +} + +.bd-callout code { + border-radius: .25rem; +} + +.bd-callout + .bd-callout { + margin-top: -.25rem; +} + +// Variations +@mixin bs-callout-variant($color) { + border-left-color: $color; + border-left-width: .25rem; + h4 { color: $color; } +} + +.fhir-card-callout-success .card, .bd-callout-success { @include bs-callout-variant($green); } +.fhir-card-callout-info .card, .bd-callout-info { @include bs-callout-variant($cyan); } +.fhir-card-callout-warning .card, .bd-callout-warning { @include bs-callout-variant($yellow); } +.fhir-card-callout-danger .card, .bd-callout-danger { @include bs-callout-variant($red); } + diff --git a/frontend/src/lib/fixtures/r4/resources/binary/exampleMarkdown.json b/frontend/src/lib/fixtures/r4/resources/binary/exampleMarkdown.json new file mode 100644 index 00000000..33ad1a42 --- /dev/null +++ b/frontend/src/lib/fixtures/r4/resources/binary/exampleMarkdown.json @@ -0,0 +1,9 @@ +{ + "resourceType": "Binary", + "id": "example", + "contentType": "text/markdown", + "securityContext": { + "reference": "DocumentReference/example" + }, + "data": "LS0tCl9fQWR2ZXJ0aXNlbWVudCA6KV9fCgotIF9fW3BpY2FdKGh0dHBzOi8vbm9kZWNhLmdpdGh1Yi5pby9waWNhL2RlbW8vKV9fIC0gaGlnaCBxdWFsaXR5IGFuZCBmYXN0IGltYWdlCiAgcmVzaXplIGluIGJyb3dzZXIuCi0gX19bYmFiZWxmaXNoXShodHRwczovL2dpdGh1Yi5jb20vbm9kZWNhL2JhYmVsZmlzaC8pX18gLSBkZXZlbG9wZXIgZnJpZW5kbHkKICBpMThuIHdpdGggcGx1cmFscyBzdXBwb3J0IGFuZCBlYXN5IHN5bnRheC4KCllvdSB3aWxsIGxpa2UgdGhvc2UgcHJvamVjdHMhCgotLS0KCiMgaDEgSGVhZGluZyA4LSkKIyMgaDIgSGVhZGluZwojIyMgaDMgSGVhZGluZwojIyMjIGg0IEhlYWRpbmcKIyMjIyMgaDUgSGVhZGluZwojIyMjIyMgaDYgSGVhZGluZwoKCiMjIEhvcml6b250YWwgUnVsZXMKCl9fXwoKLS0tCgoqKioKCgojIyBUeXBvZ3JhcGhpYyByZXBsYWNlbWVudHMKCkVuYWJsZSB0eXBvZ3JhcGhlciBvcHRpb24gdG8gc2VlIHJlc3VsdC4KCihjKSAoQykgKHIpIChSKSAodG0pIChUTSkgKHApIChQKSArLQoKdGVzdC4uIHRlc3QuLi4gdGVzdC4uLi4uIHRlc3Q/Li4uLi4gdGVzdCEuLi4uCgohISEhISEgPz8/PyAsLCAgLS0gLS0tCgoiU21hcnR5cGFudHMsIGRvdWJsZSBxdW90ZXMiIGFuZCAnc2luZ2xlIHF1b3RlcycKCgojIyBFbXBoYXNpcwoKKipUaGlzIGlzIGJvbGQgdGV4dCoqCgpfX1RoaXMgaXMgYm9sZCB0ZXh0X18KCipUaGlzIGlzIGl0YWxpYyB0ZXh0KgoKX1RoaXMgaXMgaXRhbGljIHRleHRfCgp+flN0cmlrZXRocm91Z2h+fgoKCiMjIEJsb2NrcXVvdGVzCgoKPiBCbG9ja3F1b3RlcyBjYW4gYWxzbyBiZSBuZXN0ZWQuLi4KPj4gLi4uYnkgdXNpbmcgYWRkaXRpb25hbCBncmVhdGVyLXRoYW4gc2lnbnMgcmlnaHQgbmV4dCB0byBlYWNoIG90aGVyLi4uCj4gPiA+IC4uLm9yIHdpdGggc3BhY2VzIGJldHdlZW4gYXJyb3dzLgoKCiMjIExpc3RzCgpVbm9yZGVyZWQKCisgQ3JlYXRlIGEgbGlzdCBieSBzdGFydGluZyBhIGxpbmUgd2l0aCBgK2AsIGAtYCwgb3IgYCpgCisgU3ViLWxpc3RzIGFyZSBtYWRlIGJ5IGluZGVudGluZyAyIHNwYWNlczoKICAtIE1hcmtlciBjaGFyYWN0ZXIgY2hhbmdlIGZvcmNlcyBuZXcgbGlzdCBzdGFydDoKICAgICogQWMgdHJpc3RpcXVlIGxpYmVybyB2b2x1dHBhdCBhdAogICAgKyBGYWNpbGlzaXMgaW4gcHJldGl1bSBuaXNsIGFsaXF1ZXQKICAgIC0gTnVsbGEgdm9sdXRwYXQgYWxpcXVhbSB2ZWxpdAorIFZlcnkgZWFzeSEKCk9yZGVyZWQKCjEuIExvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0CjIuIENvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdAozLiBJbnRlZ2VyIG1vbGVzdGllIGxvcmVtIGF0IG1hc3NhCgoKMS4gWW91IGNhbiB1c2Ugc2VxdWVudGlhbCBudW1iZXJzLi4uCjEuIC4uLm9yIGtlZXAgYWxsIHRoZSBudW1iZXJzIGFzIGAxLmAKClN0YXJ0IG51bWJlcmluZyB3aXRoIG9mZnNldDoKCjU3LiBmb28KMS4gYmFyCgoKIyMgQ29kZQoKSW5saW5lIGBjb2RlYAoKSW5kZW50ZWQgY29kZQoKICAgIC8vIFNvbWUgY29tbWVudHMKICAgIGxpbmUgMSBvZiBjb2RlCiAgICBsaW5lIDIgb2YgY29kZQogICAgbGluZSAzIG9mIGNvZGUKCgpCbG9jayBjb2RlICJmZW5jZXMiCgo=" +} diff --git a/frontend/src/lib/fixtures/r4/resources/binary/exampleRtf.json b/frontend/src/lib/fixtures/r4/resources/binary/exampleRtf.json new file mode 100644 index 00000000..bfef2408 --- /dev/null +++ b/frontend/src/lib/fixtures/r4/resources/binary/exampleRtf.json @@ -0,0 +1,9 @@ +{ + "resourceType": "Binary", + "id": "example", + "contentType": "application/pdf", + "securityContext": { + "reference": "DocumentReference/example" + }, + "data": "e1xydGYxXGFuc2lcZGVmZjNcYWRlZmxhbmcxMDI1CntcZm9udHRibHtcZjBcZnJvbWFuXGZwcnEyXGZjaGFyc2V0MCBUaW1lcyBOZXcgUm9tYW47fXtcZjFcZnJvbWFuXGZwcnEyXGZjaGFyc2V0MiBTeW1ib2w7fXtcZjJcZnN3aXNzXGZwcnEyXGZjaGFyc2V0MCBBcmlhbDt9e1xmM1xmcm9tYW5cZnBycTJcZmNoYXJzZXQwIExpYmVyYXRpb24gU2VyaWZ7XCpcZmFsdCBUaW1lcyBOZXcgUm9tYW59O317XGY0XGZyb21hblxmcHJxMlxmY2hhcnNldDAgTGliZXJhdGlvbiBTYW5ze1wqXGZhbHQgQXJpYWx9O317XGY1XGZyb21hblxmcHJxMlxmY2hhcnNldDAgU3ltYm9sO317XGY2XGZyb21hblxmcHJxMlxmY2hhcnNldDAgT3BlblN5bWJvbHtcKlxmYWx0IEFyaWFsIFVuaWNvZGUgTVN9O317XGY3XGZyb21hblxmcHJxMlxmY2hhcnNldDAgRGVqYVZ1IFNhbnM7fXtcZjhcZnJvbWFuXGZwcnEyXGZjaGFyc2V0MCBPcGVuIFNhbnN7XCpcZmFsdCBBcmlhbH07fXtcZjlcZm5pbFxmcHJxMlxmY2hhcnNldDAgRHJvaWQgU2FucyBGYWxsYmFjazt9e1xmMTBcZm5pbFxmcHJxMlxmY2hhcnNldDAgT3BlblN5bWJvbHtcKlxmYWx0IEFyaWFsIFVuaWNvZGUgTVN9O317XGYxMVxmbmlsXGZwcnEyXGZjaGFyc2V0MCBEZWphVnUgU2Fuczt9e1xmMTJcZm5pbFxmcHJxMlxmY2hhcnNldDAgT3BlbiBTYW5ze1wqXGZhbHQgQXJpYWx9O317XGYxM1xmbmlsXGZwcnEyXGZjaGFyc2V0MCBGcmVlU2Fuczt9e1xmMTRcZm5pbFxmcHJxMlxmY2hhcnNldDAgU3ltYm9sO319CntcY29sb3J0Ymw7XHJlZDBcZ3JlZW4wXGJsdWUwO1xyZWQwXGdyZWVuMFxibHVlMjU1O1xyZWQwXGdyZWVuMjU1XGJsdWUyNTU7XHJlZDBcZ3JlZW4yNTVcYmx1ZTA7XHJlZDI1NVxncmVlbjBcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMFxibHVlMDtccmVkMjU1XGdyZWVuMjU1XGJsdWUwO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMFxncmVlbjBcYmx1ZTEyODtccmVkMFxncmVlbjEyOFxibHVlMTI4O1xyZWQwXGdyZWVuMTI4XGJsdWUwO1xyZWQxMjhcZ3JlZW4wXGJsdWUxMjg7XHJlZDEyOFxncmVlbjBcYmx1ZTA7XHJlZDEyOFxncmVlbjEyOFxibHVlMDtccmVkMTI4XGdyZWVuMTI4XGJsdWUxMjg7XHJlZDE5MlxncmVlbjE5MlxibHVlMTkyO1xyZWQwXGdyZWVuMFxibHVlMTA7XHJlZDBcZ3JlZW4wXGJsdWUxO30Ke1xzdHlsZXNoZWV0e1xzMFxzbmV4dDBccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGtlcm5pbmcwXGxvY2hcZjNcZnMyNFxsYW5nMTAzMyBOb3JtYWw7fQp7XHMxXHNiYXNlZG9uNTBcc25leHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMjQwXHNhMTIwXGtlZXBuXGx0cnBhclxjZjE3XGJcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMzZcYWxhbmcxMDgxXGFiXGxvY2hcZjRcZnMzNlxsYW5nMTAzMyBIZWFkaW5nIDE7fQp7XHMyXHNiYXNlZG9uNTBcc25leHQyXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMjAwXHNhMTIwXGtlZXBuXGx0cnBhclxjZjE3XGJcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMzJcYWxhbmcxMDgxXGFiXGxvY2hcZjRcZnMzMlxsYW5nMTAzMyBIZWFkaW5nIDI7fQp7XHMzXHNiYXNlZG9uNTBcc25leHQzXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMTQwXHNhMTIwXGtlZXBuXGx0cnBhclxjZjE1XGJcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjhcYWxhbmcxMDgxXGFiXGxvY2hcZjRcZnMyOFxsYW5nMTAzMyBIZWFkaW5nIDM7fQp7XCpcY3MxNVxzbmV4dDE1IFdXOE51bTF6MDt9CntcKlxjczE2XHNuZXh0MTYgV1c4TnVtMXoxO30Ke1wqXGNzMTdcc25leHQxNyBXVzhOdW0xejI7fQp7XCpcY3MxOFxzbmV4dDE4IFdXOE51bTF6Mzt9CntcKlxjczE5XHNuZXh0MTkgV1c4TnVtMXo0O30Ke1wqXGNzMjBcc25leHQyMCBXVzhOdW0xejU7fQp7XCpcY3MyMVxzbmV4dDIxIFdXOE51bTF6Njt9CntcKlxjczIyXHNuZXh0MjIgV1c4TnVtMXo3O30Ke1wqXGNzMjNcc25leHQyMyBXVzhOdW0xejg7fQp7XCpcY3MyNFxzbmV4dDI0IFdXOE51bTJ6MDt9CntcKlxjczI1XHNuZXh0MjUgV1c4TnVtMnoxO30Ke1wqXGNzMjZcc25leHQyNiBXVzhOdW0yejI7fQp7XCpcY3MyN1xzbmV4dDI3IFdXOE51bTJ6Mzt9CntcKlxjczI4XHNuZXh0MjggV1c4TnVtMno0O30Ke1wqXGNzMjlcc25leHQyOSBXVzhOdW0yejU7fQp7XCpcY3MzMFxzbmV4dDMwIFdXOE51bTJ6Njt9CntcKlxjczMxXHNuZXh0MzEgV1c4TnVtMno3O30Ke1wqXGNzMzJcc25leHQzMiBXVzhOdW0yejg7fQp7XCpcY3MzM1xzbmV4dDMzXGRiY2hcYWYxMFxsb2NoXGY1IFdXOE51bTN6MDt9CntcKlxjczM0XHNuZXh0MzRcZGJjaFxhZjEwXGxvY2hcZjYgV1c4TnVtM3oxO30Ke1wqXGNzMzVcc25leHQzNVxkYmNoXGFmMTBcZGJjaFxhZjEwXGxvY2hcZjYgQnVsbGV0czt9CntcKlxjczM2XHNuZXh0MzZcY2Y5XHVsXHVsYzBcbGFuZ2ZlMjU1XGFsYW5nMjU1XGxhbmcyNTUgSW50ZXJuZXQgTGluazt9CntcKlxjczM3XHNuZXh0MzdcY2YxM1x1bFx1bGMwXGxhbmdmZTI1NVxhbGFuZzI1NVxsYW5nMjU1IFZpc2l0ZWQgSW50ZXJuZXQgTGluazt9CntcKlxjczM4XHNuZXh0MzhcZGJjaFxhZjE0IExpc3RMYWJlbCAxO30Ke1wqXGNzMzlcc25leHQzOVxkYmNoXGFmMTAgTGlzdExhYmVsIDI7fQp7XCpcY3M0MFxzbmV4dDQwXGIwXGRiY2hcYWYxNFxsb2NoXGY3XGZzMjEgTGlzdExhYmVsIDM7fQp7XCpcY3M0MVxzbmV4dDQxXGRiY2hcYWYxMCBMaXN0TGFiZWwgNDt9CntcKlxjczQyXHNuZXh0NDJcZGJjaFxhZjEwIExpc3RMYWJlbCA1O30Ke1wqXGNzNDNcc25leHQ0M1xkYmNoXGFmMTQgTGlzdExhYmVsIDY7fQp7XCpcY3M0NFxzbmV4dDQ0XGRiY2hcYWYxMCBMaXN0TGFiZWwgNzt9CntcKlxjczQ1XHNuZXh0NDVcZGJjaFxhZjEwIExpc3RMYWJlbCA4O30Ke1wqXGNzNDZcc25leHQ0NlxkYmNoXGFmMTQgTGlzdExhYmVsIDk7fQp7XCpcY3M0N1xzbmV4dDQ3XGRiY2hcYWYxMCBMaXN0TGFiZWwgMTA7fQp7XCpcY3M0OFxzbmV4dDQ4XGRiY2hcYWYxMCBMaXN0TGFiZWwgMTE7fQp7XCpcY3M0OVxzbmV4dDQ5XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxsb2NoXGY3XGZzMjEgTGlzdExhYmVsIDEyO30Ke1xzNTBcc2Jhc2Vkb24wXHNuZXh0NTFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IyNDBcc2ExMjBca2VlcG5cbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjhcYWxhbmcxMDgxXGxvY2hcZjRcZnMyOFxsYW5nMTAzMyBIZWFkaW5nO30Ke1xzNTFcc2Jhc2Vkb24wXHNuZXh0NTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzMyBUZXh0IEJvZHk7fQp7XHM1MlxzYmFzZWRvbjUxXHNuZXh0NTJcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzMyBMaXN0O30Ke1xzNTNcc2Jhc2Vkb24wXHNuZXh0NTNccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IxMjBcc2ExMjBcbm9saW5lXGx0cnBhclxjZjE3XGlcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGFpXGxvY2hcZjNcZnMyNFxsYW5nMTAzMyBDYXB0aW9uO30Ke1xzNTRcc2Jhc2Vkb24wXHNuZXh0NTRccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcbm9saW5lXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzMgSW5kZXg7fQp7XHM1NVxzYmFzZWRvbjBcc25leHQ1NVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxsaTU2N1xyaTU2N1xsaW41NjdccmluNTY3XGZpMFxzYjBcc2EyODNcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzMyBRdW90YXRpb25zO30Ke1xzNTZcc2Jhc2Vkb241MFxzbmV4dDU2XHFjXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMjQwXHNhMTIwXGtlZXBuXGx0cnBhclxjZjE3XGJcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzNTZcYWxhbmcxMDgxXGFiXGxvY2hcZjRcZnM1NlxsYW5nMTAzMyBUaXRsZTt9CntcczU3XHNiYXNlZG9uNTBcc25leHQ1N1xxY1xub3dpZGN0bHBhclxoeXBocGFyMFxzYjYwXHNhMTIwXGtlZXBuXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczM2XGFsYW5nMTA4MVxsb2NoXGY0XGZzMzZcbGFuZzEwMzMgU3VidGl0bGU7fQp7XHM1OFxzYmFzZWRvbjBcc25leHQ1OFxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxub2xpbmVcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzMyBUYWJsZSBDb250ZW50czt9CntcczU5XHNiYXNlZG9uNThcc25leHQ1OVxxY1xub3dpZGN0bHBhclxoeXBocGFyMFxub2xpbmVcbHRycGFyXGNmMTdcYlxkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcYWJcbG9jaFxmM1xmczI0XGxhbmcxMDMzIFRhYmxlIEhlYWRpbmc7fQp9e1wqXGxpc3R0YWJsZXtcbGlzdFxsaXN0dGVtcGxhdGVpZDEKe1xsaXN0bGV2ZWxcbGV2ZWxuZmMyNTVcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzJ7XGxldmVsdGV4dCBcJzAwO317XGxldmVsbnVtYmVyczt9XGZpLTQzMlxsaTc5Mn0Ke1xsaXN0bGV2ZWxcbGV2ZWxuZmMyNTVcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzJ7XGxldmVsdGV4dCBcJzAwO317XGxldmVsbnVtYmVyczt9XGZpLTU3NlxsaTkzNn0Ke1xsaXN0bGV2ZWxcbGV2ZWxuZmMyNTVcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzJ7XGxldmVsdGV4dCBcJzAwO317XGxldmVsbnVtYmVyczt9XGZpLTcyMFxsaTEwODB9CntcbGlzdGxldmVsXGxldmVsbmZjMjU1XGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cye1xsZXZlbHRleHQgXCcwMDt9e1xsZXZlbG51bWJlcnM7fVxmaS04NjRcbGkxMjI0fQp7XGxpc3RsZXZlbFxsZXZlbG5mYzI1NVxsZXZlbGpjMFxsZXZlbHN0YXJ0YXQxXGxldmVsZm9sbG93MntcbGV2ZWx0ZXh0IFwnMDA7fXtcbGV2ZWxudW1iZXJzO31cZmktMTAwOFxsaTEzNjh9CntcbGlzdGxldmVsXGxldmVsbmZjMjU1XGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cye1xsZXZlbHRleHQgXCcwMDt9e1xsZXZlbG51bWJlcnM7fVxmaS0xMTUyXGxpMTUxMn0Ke1xsaXN0bGV2ZWxcbGV2ZWxuZmMyNTVcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzJ7XGxldmVsdGV4dCBcJzAwO317XGxldmVsbnVtYmVyczt9XGZpLTEyOTZcbGkxNjU2fQp7XGxpc3RsZXZlbFxsZXZlbG5mYzI1NVxsZXZlbGpjMFxsZXZlbHN0YXJ0YXQxXGxldmVsZm9sbG93MntcbGV2ZWx0ZXh0IFwnMDA7fXtcbGV2ZWxudW1iZXJzO31cZmktMTQ0MFxsaTE4MDB9CntcbGlzdGxldmVsXGxldmVsbmZjMjU1XGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cye1xsZXZlbHRleHQgXCcwMDt9e1xsZXZlbG51bWJlcnM7fVxmaS0xNTg0XGxpMTk0NH1cbGlzdGlkMX0Ke1xsaXN0XGxpc3R0ZW1wbGF0ZWlkMgp7XGxpc3RsZXZlbFxsZXZlbG5mYzIzXGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cwe1xsZXZlbHRleHQgXCcwMVx1NjE2MjMgPzt9e1xsZXZlbG51bWJlcnM7fVxmMTVcYjBcZGJjaFxhZjE0XGZpLTM2MFxsaTcyMH0Ke1xsaXN0bGV2ZWxcbGV2ZWxuZmMyM1xsZXZlbGpjMFxsZXZlbHN0YXJ0YXQxXGxldmVsZm9sbG93MHtcbGV2ZWx0ZXh0IFwnMDFcdTk3MDIgPzt9e1xsZXZlbG51bWJlcnM7fVxmMTZcZGJjaFxhZjEwXGZpLTM2MFxsaTEwODB9CntcbGlzdGxldmVsXGxldmVsbmZjMjNcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzB7XGxldmVsdGV4dCBcJzAxXHU5NjQyID87fXtcbGV2ZWxudW1iZXJzO31cZjE2XGRiY2hcYWYxMFxmaS0zNjBcbGkxNDQwfQp7XGxpc3RsZXZlbFxsZXZlbG5mYzIzXGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cwe1xsZXZlbHRleHQgXCcwMVx1NjE2MjMgPzt9e1xsZXZlbG51bWJlcnM7fVxmMTVcZGJjaFxhZjE0XGZpLTM2MFxsaTE4MDB9CntcbGlzdGxldmVsXGxldmVsbmZjMjNcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzB7XGxldmVsdGV4dCBcJzAxXHU5NzAyID87fXtcbGV2ZWxudW1iZXJzO31cZjE2XGRiY2hcYWYxMFxmaS0zNjBcbGkyMTYwfQp7XGxpc3RsZXZlbFxsZXZlbG5mYzIzXGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cwe1xsZXZlbHRleHQgXCcwMVx1OTY0MiA/O317XGxldmVsbnVtYmVyczt9XGYxNlxkYmNoXGFmMTBcZmktMzYwXGxpMjUyMH0Ke1xsaXN0bGV2ZWxcbGV2ZWxuZmMyM1xsZXZlbGpjMFxsZXZlbHN0YXJ0YXQxXGxldmVsZm9sbG93MHtcbGV2ZWx0ZXh0IFwnMDFcdTYxNjIzID87fXtcbGV2ZWxudW1iZXJzO31cZjE1XGRiY2hcYWYxNFxmaS0zNjBcbGkyODgwfQp7XGxpc3RsZXZlbFxsZXZlbG5mYzIzXGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cwe1xsZXZlbHRleHQgXCcwMVx1OTcwMiA/O317XGxldmVsbnVtYmVyczt9XGYxNlxkYmNoXGFmMTBcZmktMzYwXGxpMzI0MH0Ke1xsaXN0bGV2ZWxcbGV2ZWxuZmMyM1xsZXZlbGpjMFxsZXZlbHN0YXJ0YXQxXGxldmVsZm9sbG93MHtcbGV2ZWx0ZXh0IFwnMDFcdTk2NDIgPzt9e1xsZXZlbG51bWJlcnM7fVxmMTZcZGJjaFxhZjEwXGZpLTM2MFxsaTM2MDB9XGxpc3RpZDJ9CntcbGlzdFxsaXN0dGVtcGxhdGVpZDMKe1xsaXN0bGV2ZWxcbGV2ZWxuZmMyNTVcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzJ7XGxldmVsdGV4dCBcJzAwO317XGxldmVsbnVtYmVyczt9XGZpMFxsaTB9CntcbGlzdGxldmVsXGxldmVsbmZjMjU1XGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cye1xsZXZlbHRleHQgXCcwMDt9e1xsZXZlbG51bWJlcnM7fVxmaTBcbGkwfQp7XGxpc3RsZXZlbFxsZXZlbG5mYzI1NVxsZXZlbGpjMFxsZXZlbHN0YXJ0YXQxXGxldmVsZm9sbG93MntcbGV2ZWx0ZXh0IFwnMDA7fXtcbGV2ZWxudW1iZXJzO31cZmkwXGxpMH0Ke1xsaXN0bGV2ZWxcbGV2ZWxuZmMyNTVcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzJ7XGxldmVsdGV4dCBcJzAwO317XGxldmVsbnVtYmVyczt9XGZpMFxsaTB9CntcbGlzdGxldmVsXGxldmVsbmZjMjU1XGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cye1xsZXZlbHRleHQgXCcwMDt9e1xsZXZlbG51bWJlcnM7fVxmaTBcbGkwfQp7XGxpc3RsZXZlbFxsZXZlbG5mYzI1NVxsZXZlbGpjMFxsZXZlbHN0YXJ0YXQxXGxldmVsZm9sbG93MntcbGV2ZWx0ZXh0IFwnMDA7fXtcbGV2ZWxudW1iZXJzO31cZmkwXGxpMH0Ke1xsaXN0bGV2ZWxcbGV2ZWxuZmMyNTVcbGV2ZWxqYzBcbGV2ZWxzdGFydGF0MVxsZXZlbGZvbGxvdzJ7XGxldmVsdGV4dCBcJzAwO317XGxldmVsbnVtYmVyczt9XGZpMFxsaTB9CntcbGlzdGxldmVsXGxldmVsbmZjMjU1XGxldmVsamMwXGxldmVsc3RhcnRhdDFcbGV2ZWxmb2xsb3cye1xsZXZlbHRleHQgXCcwMDt9e1xsZXZlbG51bWJlcnM7fVxmaTBcbGkwfQp7XGxpc3RsZXZlbFxsZXZlbG5mYzI1NVxsZXZlbGpjMFxsZXZlbHN0YXJ0YXQxXGxldmVsZm9sbG93MntcbGV2ZWx0ZXh0IFwnMDA7fXtcbGV2ZWxudW1iZXJzO31cZmkwXGxpMH1cbGlzdGlkM30KfXtcbGlzdG92ZXJyaWRldGFibGV7XGxpc3RvdmVycmlkZVxsaXN0aWQxXGxpc3RvdmVycmlkZWNvdW50MFxsczF9e1xsaXN0b3ZlcnJpZGVcbGlzdGlkMlxsaXN0b3ZlcnJpZGVjb3VudDBcbHMyfXtcbGlzdG92ZXJyaWRlXGxpc3RpZDNcbGlzdG92ZXJyaWRlY291bnQwXGxzM319e1wqXGdlbmVyYXRvciBMaWJyZU9mZmljZS82LjAuNy4zJExpbnV4X1g4Nl82NCBMaWJyZU9mZmljZV9wcm9qZWN0LzAwbTAkQnVpbGQtM317XGluZm97XGNyZWF0aW1ceXIyMDE3XG1vOFxkeTJcaHIxMVxtaW45fXtccmV2dGltXHlyMjAxOVxtbzlcZHkyMVxocjE0XG1pbjJ9e1xwcmludGltXHlyMFxtbzBcZHkwXGhyMFxtaW4wfX17XCpcdXNlcnByb3BzfVxkZWZ0YWI3MDkKXGh5cGhhdXRvMFx2aWV3c2NhbGUxMDAKe1wqXHBnZHNjdGJsCntccGdkc2MwXHBnZHNjdXNlNDUxXHBnd3N4bjExOTA2XHBnaHN4bjE2ODM4XG1hcmdsc3huMTEzNFxtYXJncnN4bjExMzRcbWFyZ3RzeG4xMTM0XG1hcmdic3huMTEzNFxwZ2RzY254dDAgRGVmYXVsdCBTdHlsZTt9fQpcZm9ybXNoYWRle1wqXHBnZHNjbm8wfVxwYXBlcmgxNjgzOFxwYXBlcncxMTkwNlxtYXJnbDExMzRcbWFyZ3IxMTM0XG1hcmd0MTEzNFxtYXJnYjExMzRcc2VjdGRcc2Jrbm9uZVxzZWN0dW5sb2NrZWQxXHBnbmRlY1xwZ3dzeG4xMTkwNlxwZ2hzeG4xNjgzOFxtYXJnbHN4bjExMzRcbWFyZ3JzeG4xMTM0XG1hcmd0c3huMTEzNFxtYXJnYnN4bjExMzRcZnRuYmpcZnRuc3RhcnQxXGZ0bnJzdGNvbnRcZnRubmFyXGFlbmRkb2NcYWZ0bnJzdGNvbnRcYWZ0bnN0YXJ0MVxhZnRubnJsY1xodG1hdXRzcAp7XCpcZnRuc2VwXGNoZnRuc2VwfVx2aWV3YmtzcDF7XCpcYmFja2dyb3VuZHtcc2hwe1wqXHNocGluc3R7XHNwe1xzbiBzaGFwZVR5cGV9e1xzdiAxfX17XHNwe1xzbiBmaWxsQ29sb3J9e1xzdiAxNjc3NzIxNX19fX19XHBnbmRlY1xwYXJkXHBsYWluIFxzNTZccWNcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IyNDBcc2ExMjBca2VlcG5cbHRycGFyXGNmMTdcYlxkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnM1NlxhbGFuZzEwODFcYWJcbG9jaFxmNFxmczU2XGxhbmcxMDMzXHFjXHNiMjQwXHNhMTIwe1xjYnBhdDhcY2JwYXQ4XGZzMjFccnRsY2ggXGx0cmNoXGxvY2gKTG9yZW0gaXBzdW0gfQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKClxwYXIgXHBhcmRccGxhaW4gXHMxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMjQwXHNhMTIwXGtlZXBuXGx0cnBhclxjZjE3XGJcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMzZcYWxhbmcxMDgxXGFiXGxvY2hcZjRcZnMzNlxsYW5nMTAzM3tcbGlzdHRleHRccGFyZFxwbGFpbiB9XGlsdmwwXGxzMSBcbGk3OTJccmkwXGxpbjc5MlxyaW4wXGZpLTQzMlxsaTBccmkwXGxpbjBccmluMFxmaS00MzJcc2IyNDBcc2ExMjBca2VlcG57XHJ0bGNoIFxsdHJjaFxsb2NoCkxvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0LCBjb25zZWN0ZXR1ciBhZGlwaXNjaW5nIGVsaXQuIE51bmMgYWMgZmF1Y2lidXMgb2Rpby4gfQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjV7XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKVmVzdGlidWx1bSBuZXF1ZSBtYXNzYSwgc2NlbGVyaXNxdWUgc2l0IGFtZXQgbGlndWxhIGV1LCBjb25ndWUgbW9sZXN0aWUgbWkuIFByYWVzZW50IHV0IHZhcml1cyBzZW0uIE51bGxhbSBhdCBwb3J0dGl0b3IgYXJjdSwgbmVjIGxhY2luaWEgbmlzaS4gVXQgYWMgZG9sb3Igdml0YWUgb2RpbyBpbnRlcmR1bSBjb25kaW1lbnR1bS4gfXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYlxkYmNoXGFmMTFcYWJccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3ClZpdmFtdXMgZGFwaWJ1cyBzb2RhbGVzIGV4LCB2aXRhZSBtYWxlc3VhZGEgaXBzdW0gY3Vyc3VzIGNvbnZhbGxpcy4gTWFlY2VuYXMgc2VkIGVnZXN0YXMgbnVsbGEsIGFjIGNvbmRpbWVudHVtIG9yY2kuIH17XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKTWF1cmlzIGRpYW0gZmVsaXMsIHZ1bHB1dGF0ZSBhYyBzdXNjaXBpdCBldCwgaWFjdWxpcyBub24gZXN0LiBDdXJhYml0dXIgc2VtcGVyIGFyY3UgYWMgbGlndWxhIHNlbXBlciwgbmVjIGx1Y3R1cyBuaXNsIGJsYW5kaXQuIEludGVnZXIgbGFjaW5pYSBhbnRlIGFjIGxpYmVybyBsb2JvcnRpcyBpbXBlcmRpZXQuIH17XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGlcYjBcZGJjaFxhZjExXGFpXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpOdWxsYW0gbW9sbGlzIGNvbnZhbGxpcyBpcHN1bSwgYWMgYWNjdW1zYW4gbnVuYyB2ZWhpY3VsYSB2aXRhZS4gfXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpOdWxsYSBlZ2V0IGp1c3RvIGluIGZlbGlzIHRyaXN0aXF1ZSBmcmluZ2lsbGEuIE1vcmJpIHNpdCBhbWV0IHRvcnRvciBxdWlzIHJpc3VzIGF1Y3RvciBjb25kaW1lbnR1bS4gTW9yYmkgaW4gdWxsYW1jb3JwZXIgZWxpdC4gTnVsbGEgaWFjdWxpcyB0ZWxsdXMgc2l0IGFtZXQgbWF1cmlzIHRlbXB1cyBmcmluZ2lsbGEufQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1e1xzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3Ck1hZWNlbmFzIG1hdXJpcyBsZWN0dXMsIGxvYm9ydGlzIGV0IHB1cnVzIG1hdHRpcywgYmxhbmRpdCBkaWN0dW0gdGVsbHVzLn0KXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzN7XGxpc3R0ZXh0XHBhcmRccGxhaW4gXGIwXGRiY2hcYWYxNFxsb2NoXGY3XGZzMjEgXHU2MTYyM1wnM2ZcdGFifVxpbHZsMFxsczIgXGxpNzIwXHJpMFxsaW43MjBccmluMFxmaS0zNjBccWpcd2lkY3RscGFyXHNiMFxzYTIyNXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYlxkYmNoXGFmMTFcYWJccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3Ck1hZWNlbmFzIG5vbiBsb3JlbSBxdWlzIHRlbGx1cyBwbGFjZXJhdCB2YXJpdXMuIH0KXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzN7XGxpc3R0ZXh0XHBhcmRccGxhaW4gXGIwXGRiY2hcYWYxNFxsb2NoXGY3XGZzMjEgXHU2MTYyM1wnM2ZcdGFifVxpbHZsMFxsczIgXGxpNzIwXHJpMFxsaW43MjBccmluMFxmaS0zNjBccWpcd2lkY3RscGFyXHNiMFxzYTIyNXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaVxiMFxkYmNoXGFmMTFcYWlccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3Ck51bGxhIGZhY2lsaXNpLiB9ClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMze1xsaXN0dGV4dFxwYXJkXHBsYWluIFxiMFxkYmNoXGFmMTRcbG9jaFxmN1xmczIxIFx1NjE2MjNcJzNmXHRhYn1caWx2bDBcbHMyIFxsaTcyMFxyaTBcbGluNzIwXHJpbjBcZmktMzYwXHFqXHdpZGN0bHBhclxzYjBcc2EyMjV7XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXHVsXHVsYzBcYjBcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpBZW5lYW4gY29uZ3VlIGZyaW5naWxsYSBqdXN0byB1dCBhbGlxdWFtLiB9ClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMze1xsaXN0dGV4dFxwYXJkXHBsYWluIFxiMFxkYmNoXGFmMTRcbG9jaFxmN1xmczIxIFx1NjE2MjNcJzNmXHRhYn1caWx2bDBcbHMyIFxsaTcyMFxyaTBcbGluNzIwXHJpbjBcZmktMzYwXHFqXHdpZGN0bHBhclxzYjBcc2EyMjV7e1xmaWVsZHtcKlxmbGRpbnN0IEhZUEVSTElOSyAiaHR0cHM6Ly9wcm9kdWN0cy5vZmZpY2UuY29tL2VuLXVzL3dvcmQiIH17XGZsZHJzbHQge1xjczM2XGNmOVx1bFx1bGMwXGxhbmdmZTI1NVxhbGFuZzI1NVxsYW5nMjU1XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKTWF1cmlzIGlkIGV4IGVyYXQuIH17fX19XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKTnVuYyB2dWxwdXRhdGUgbmVxdWUgdml0YWUganVzdG8gZmFjaWxpc2lzLCBub24gY29uZGltZW50dW0gYW50ZSBzYWdpdHRpcy4gfQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM3tcbGlzdHRleHRccGFyZFxwbGFpbiBcYjBcZGJjaFxhZjE0XGxvY2hcZjdcZnMyMSBcdTYxNjIzXCczZlx0YWJ9XGlsdmwwXGxzMiBcbGk3MjBccmkwXGxpbjcyMFxyaW4wXGZpLTM2MFxxalx3aWRjdGxwYXJcc2IwXHNhMjI1e1xzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3Ck1vcmJpIHZpdmVycmEgc2VtcGVyIGxvcmVtIG5lYyBtb2xlc3RpZS4gfQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM3tcbGlzdHRleHRccGFyZFxwbGFpbiBcYjBcZGJjaFxhZjE0XGxvY2hcZjdcZnMyMSBcdTYxNjIzXCczZlx0YWJ9XGlsdmwwXGxzMiBcbGk3MjBccmkwXGxpbjcyMFxyaW4wXGZpLTM2MFxxalx3aWRjdGxwYXJcc2IwXHNhMjI1e1xzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3Ck1hZWNlbmFzIHRpbmNpZHVudCBlc3QgZWZmaWNpdHVyIGxpZ3VsYSBldWlzbW9kLCBzaXQgYW1ldCBvcm5hcmUgZXN0IHZ1bHB1dGF0ZS59ClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwoKXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNVxzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CgpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1e1xzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CkluIG5vbiBtYXVyaXMganVzdG8uIER1aXMgdmVoaWN1bGEgbWkgdmVsIG1pIHByZXRpdW0sIGEgdml2ZXJyYSBlcmF0IGVmZmljaXR1ci4gQ3JhcyBhbGlxdWFtIGVzdCBhYyBlcm9zIHZhcml1cywgaWQgaWFjdWxpcyBkdWkgYXVjdG9yLiBEdWlzIHByZXRpdW0gbmVxdWUgbGlndWxhLCBldCBwdWx2aW5hciBtaSBwbGFjZXJhdCBldC4gTnVsbGEgbmVjIG51bmMgc2l0IGFtZXQgbnVuYyBwb3N1ZXJlIHZlc3RpYnVsdW0uIFV0IGlkIG5lcXVlIGVnZXQgdG9ydG9yIG1hdHRpcyB0cmlzdGlxdWUuIERvbmVjIGFudGUgZXN0LCBibGFuZGl0IHNpdCBhbWV0IHRyaXN0aXF1ZSB2ZWwsIGxhY2luaWEgcHVsdmluYXIgYXJjdS4gUGVsbGVudGVzcXVlIHNjZWxlcmlzcXVlIGZlcm1lbnR1bSBlcmF0LCBpZCBwb3N1ZXJlIGp1c3RvIHB1bHZpbmFyIHV0LiBDcmFzIGlkIGVyb3Mgc2VkIGVuaW0gYWxpcXVhbSBsb2JvcnRpcy4gU2VkIGxvYm9ydGlzIG5pc2wgdXQgZXJvcyBlZmZpY2l0dXIgdGluY2lkdW50LiBDcmFzIGp1c3RvIG1pLCBwb3J0dGl0b3IgcXVpcyBtYXR0aXMgdmVsLCB1bHRyaWNpZXMgdXQgcHVydXMuIFV0IGZhY2lsaXNpcyBldCBsYWN1cyBldSBjdXJzdXMufQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1e1xzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CkluIGVsZWlmZW5kIHZlbGl0IHZpdGFlIGxpYmVybyBzb2xsaWNpdHVkaW4gZXVpc21vZC4gRnVzY2Ugdml0YWUgdmVzdGlidWx1bSB2ZWxpdC4gUGVsbGVudGVzcXVlIHZ1bHB1dGF0ZSBsZWN0dXMgcXVpcyBwZWxsZW50ZXNxdWUgY29tbW9kby4gQWxpcXVhbSBlcmF0IHZvbHV0cGF0LiBWZXN0aWJ1bHVtIGluIGVnZXN0YXMgdmVsaXQuIFBlbGxlbnRlc3F1ZSBmZXJtZW50dW0gbmlzbCB2aXRhZSBmcmluZ2lsbGEgdmVuZW5hdGlzLiBFdGlhbSBpZCBtYXVyaXMgdml0YWUgb3JjaSBtYXhpbXVzIHVsdHJpY2llcy4gfQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKClxwYXIgXHBhcmRccGxhaW4gXHMxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMjQwXHNhMTIwXGtlZXBuXGx0cnBhclxjZjE3XGJcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMzZcYWxhbmcxMDgxXGFiXGxvY2hcZjRcZnMzNlxsYW5nMTAzM3tcbGlzdHRleHRccGFyZFxwbGFpbiB9XGlsdmwwXGxzMSBcbGk3OTJccmkwXGxpbjc5MlxyaW4wXGZpLTQzMlxsaTBccmkwXGxpbjBccmluMFxmaS00MzJcc2IyNDBcc2ExMjBca2VlcG57XHJ0bGNoIFxsdHJjaFxsb2NoCkNyYXMgZnJpbmdpbGxhIGlwc3VtIG1hZ25hLCBpbiBmcmluZ2lsbGEgZHVpIGNvbW1vZG8gYS59ClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwoKXHBhciBcdHJvd2RcdHJxbFx0cmxlZnQ1M1xsdHJyb3dcdHJyaDQ1MFx0cnBhZGRmdDNcdHJwYWRkdDBcdHJwYWRkZmwzXHRycGFkZGwwXHRycGFkZGZiM1x0cnBhZGRiMFx0cnBhZGRmcjNcdHJwYWRkcjBcY2xicmRydFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg3NzBcY2xicmRydFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg2NDM0XGNsYnJkcnRcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZmwzXGNscGFkbDU1XGNsYnJkcmxcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZnQzXGNscGFkdDUxXGNsYnJkcmJcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZmIzXGNscGFkYjU1XGNscGFkZnIzXGNscGFkcjU1XGNsY2JwYXQ4XGNlbGx4Nzk5MlxjbGJyZHJ0XGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZsM1xjbHBhZGw1NVxjbGJyZHJsXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZ0M1xjbHBhZHQ1MVxjbGJyZHJiXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZiM1xjbHBhZGI1NVxjbGJyZHJyXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZyM1xjbHBhZHI1NVxjbGNicGF0OFxjZWxseDk2OTBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNcaW50YmxccWxcd2lkY3RscGFyXHNiMFxzYTIyNVxzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3ClxjZWxsXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXGludGJsXHFsXHdpZGN0bHBhclxzYjBcc2EyMjV7XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKTG9yZW0gaXBzdW19XGNlbGxccGFyZFxwbGFpbiBcczU4XHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXG5vbGluZVxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXGludGJsXHFse1xkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CkxvcmVtIGlwc3VtfVxjZWxsXHBhcmRccGxhaW4gXHM1OFxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxub2xpbmVcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xpbnRibFxxbHtcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpMb3JlbSBpcHN1bX1cY2VsbFxyb3dccGFyZFx0cm93ZFx0cnFsXHRybGVmdDUzXGx0cnJvd1x0cnBhZGRmdDNcdHJwYWRkdDBcdHJwYWRkZmwzXHRycGFkZGwwXHRycGFkZGZiM1x0cnBhZGRiMFx0cnBhZGRmcjNcdHJwYWRkcjBcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg3NzBcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg2NDM0XGNscGFkZmwzXGNscGFkbDU1XGNsYnJkcmxcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZnQzXGNscGFkdDUxXGNsYnJkcmJcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZmIzXGNscGFkYjU1XGNscGFkZnIzXGNscGFkcjU1XGNsY2JwYXQ4XGNlbGx4Nzk5MlxjbHBhZGZsM1xjbHBhZGw1NVxjbGJyZHJsXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZ0M1xjbHBhZHQ1MVxjbGJyZHJiXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZiM1xjbHBhZGI1NVxjbGJyZHJyXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZyM1xjbHBhZHI1NVxjbGNicGF0OFxjZWxseDk2OTBccGFyZFxwbGFpbiBcczU4XHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXG5vbGluZVxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXGludGJsXHFse1xkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CjF9XGNlbGxccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNcaW50YmxccWxcd2lkY3RscGFyXHNiMFxzYTIyNXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpJbiBlbGVpZmVuZCB2ZWxpdCB2aXRhZSBsaWJlcm8gc29sbGljaXR1ZGluIGV1aXNtb2QufVxjZWxsXHBhcmRccGxhaW4gXHM1OFxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxub2xpbmVcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xpbnRibFxxbHtcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpMb3JlbX1cY2VsbFxwYXJkXHBsYWluIFxzNThccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcbm9saW5lXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNcaW50YmxccWxcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpcY2VsbFxyb3dccGFyZFx0cm93ZFx0cnFsXHRybGVmdDUzXGx0cnJvd1x0cnBhZGRmdDNcdHJwYWRkdDBcdHJwYWRkZmwzXHRycGFkZGwwXHRycGFkZGZiM1x0cnBhZGRiMFx0cnBhZGRmcjNcdHJwYWRkcjBcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg3NzBcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg2NDM0XGNscGFkZmwzXGNscGFkbDU1XGNsYnJkcmxcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZnQzXGNscGFkdDUxXGNsYnJkcmJcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZmIzXGNscGFkYjU1XGNscGFkZnIzXGNscGFkcjU1XGNsY2JwYXQ4XGNlbGx4Nzk5MlxjbHBhZGZsM1xjbHBhZGw1NVxjbGJyZHJsXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZ0M1xjbHBhZHQ1MVxjbGJyZHJiXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZiM1xjbHBhZGI1NVxjbGJyZHJyXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZyM1xjbHBhZHI1NVxjbGNicGF0OFxjZWxseDk2OTBccGFyZFxwbGFpbiBcczU4XHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXG5vbGluZVxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXGludGJsXHFse1xkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CjJ9XGNlbGxccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNcaW50YmxccWxcd2lkY3RscGFyXHNiMFxzYTIyNXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpDcmFzIGZyaW5naWxsYSBpcHN1bSBtYWduYSwgaW4gZnJpbmdpbGxhIGR1aSBjb21tb2RvIGEufVxjZWxsXHBhcmRccGxhaW4gXHM1OFxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxub2xpbmVcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xpbnRibFxxbHtcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpJcHN1bX1cY2VsbFxwYXJkXHBsYWluIFxzNThccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcbm9saW5lXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNcaW50YmxccWxcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpcY2VsbFxyb3dccGFyZFx0cm93ZFx0cnFsXHRybGVmdDUzXGx0cnJvd1x0cnBhZGRmdDNcdHJwYWRkdDBcdHJwYWRkZmwzXHRycGFkZGwwXHRycGFkZGZiM1x0cnBhZGRiMFx0cnBhZGRmcjNcdHJwYWRkcjBcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg3NzBcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg2NDM0XGNscGFkZmwzXGNscGFkbDU1XGNsYnJkcmxcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZnQzXGNscGFkdDUxXGNsYnJkcmJcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZmIzXGNscGFkYjU1XGNscGFkZnIzXGNscGFkcjU1XGNsY2JwYXQ4XGNlbGx4Nzk5MlxjbHBhZGZsM1xjbHBhZGw1NVxjbGJyZHJsXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZ0M1xjbHBhZHQ1MVxjbGJyZHJiXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZiM1xjbHBhZGI1NVxjbGJyZHJyXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZyM1xjbHBhZHI1NVxjbGNicGF0OFxjZWxseDk2OTBccGFyZFxwbGFpbiBcczU4XHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXG5vbGluZVxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXGludGJsXHFse1xkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CjN9XGNlbGxccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNcaW50YmxccWxcd2lkY3RscGFyXHNiMFxzYTIyNXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjExXGFiXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpGdXNjZSB2aXRhZSB2ZXN0aWJ1bHVtIHZlbGl0LiB9XGNlbGxccGFyZFxwbGFpbiBcczU4XHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXG5vbGluZVxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXGludGJsXHFse1xkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CkxvcmVtfVxjZWxsXHBhcmRccGxhaW4gXHM1OFxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxub2xpbmVcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xpbnRibFxxbFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3ClxjZWxsXHJvd1xwYXJkXHRyb3dkXHRycWxcdHJsZWZ0NTNcbHRycm93XHRycGFkZGZ0M1x0cnBhZGR0MFx0cnBhZGRmbDNcdHJwYWRkbDBcdHJwYWRkZmIzXHRycGFkZGIwXHRycGFkZGZyM1x0cnBhZGRyMFxjbHBhZGZsM1xjbHBhZGw1NVxjbGJyZHJsXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZ0M1xjbHBhZHQ1MVxjbGJyZHJiXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZiM1xjbHBhZGI1NVxjbHBhZGZyM1xjbHBhZHI1NVxjbGNicGF0OFxjZWxseDc3MFxjbHBhZGZsM1xjbHBhZGw1NVxjbGJyZHJsXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZ0M1xjbHBhZHQ1MVxjbGJyZHJiXGJyZHJzXGJyZHJ3NVxicmRyY2YxOFxjbHBhZGZiM1xjbHBhZGI1NVxjbHBhZGZyM1xjbHBhZHI1NVxjbGNicGF0OFxjZWxseDY0MzRcY2xwYWRmbDNcY2xwYWRsNTVcY2xicmRybFxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmdDNcY2xwYWR0NTFcY2xicmRyYlxicmRyc1xicmRydzVcYnJkcmNmMThcY2xwYWRmYjNcY2xwYWRiNTVcY2xwYWRmcjNcY2xwYWRyNTVcY2xjYnBhdDhcY2VsbHg3OTkyXGNscGFkZmwzXGNscGFkbDU1XGNsYnJkcmxcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZnQzXGNscGFkdDUxXGNsYnJkcmJcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZmIzXGNscGFkYjU1XGNsYnJkcnJcYnJkcnNcYnJkcnc1XGJyZHJjZjE4XGNscGFkZnIzXGNscGFkcjU1XGNsY2JwYXQ4XGNlbGx4OTY5MFxwYXJkXHBsYWluIFxzNThccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcbm9saW5lXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNcaW50YmxccWx7XGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKNH1cY2VsbFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xpbnRibFxxbFx3aWRjdGxwYXJcc2IwXHNhMjI1e1xzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTFccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY3XGhpY2hcYWY3CkV0aWFtIHZlaGljdWxhIGx1Y3R1cyBmZXJtZW50dW0ufVxjZWxsXHBhcmRccGxhaW4gXHM1OFxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxub2xpbmVcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xpbnRibFxxbHtcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpJcHN1bX1cY2VsbFxwYXJkXHBsYWluIFxzNThccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcbm9saW5lXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNcaW50YmxccWxcZGJjaFxhZjExXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmN1xoaWNoXGFmNwpcY2VsbFxyb3dccGFyZFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjV7XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMVxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjdcaGljaFxhZjcKRXRpYW0gdmVoaWN1bGEgbHVjdHVzIGZlcm1lbnR1bS4gSW4gdmVsIG1ldHVzIGNvbmd1ZSwgcHVsdmluYXIgbGVjdHVzIHZlbCwgZmVybWVudHVtIGR1aS4gTWFlY2VuYXMgYW50ZSBvcmNpLCBlZ2VzdGFzIHV0IGFsaXF1ZXQgc2l0IGFtZXQsIHNhZ2l0dGlzIGEgbWFnbmEuIEFsaXF1YW0gYW50ZSBxdWFtLCBwZWxsZW50ZXNxdWUgdXQgZGlnbmlzc2ltIHF1aXMsIGxhb3JlZXQgZWdldCBlc3QuIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gQ2xhc3MgYXB0ZW50IHRhY2l0aSBzb2Npb3NxdSBhZCBsaXRvcmEgdG9ycXVlbnQgcGVyIGNvbnViaWEgbm9zdHJhLCBwZXIgaW5jZXB0b3MgaGltZW5hZW9zLiBVdCB1bGxhbWNvcnBlciBqdXN0byBzYXBpZW4sIGluIGN1cnN1cyBsaWJlcm8gdml2ZXJyYSBlZ2V0LiBWaXZhbXVzIGF1Y3RvciBpbXBlcmRpZXQgdXJuYSwgYXQgcHVsdmluYXIgbGVvIHBvc3VlcmUgbGFvcmVldC4gU3VzcGVuZGlzc2UgbmVxdWUgbmlzbCwgZnJpbmdpbGxhIGF0IGlhY3VsaXMgc2NlbGVyaXNxdWUsIG9ybmFyZSB2ZWwgZG9sb3IuIFV0IGV0IHB1bHZpbmFyIG51bmMuIFBlbGxlbnRlc3F1ZSBmcmluZ2lsbGEgbW9sbGlzIGVmZmljaXR1ci4gTnVsbGFtIHZlbmVuYXRpcyBjb21tb2RvIGltcGVyZGlldC4gTW9yYmkgdmVsaXQgbmVxdWUsIHNlbXBlciBxdWlzIGxvcmVtIHF1aXMsIGVmZmljaXR1ciBkaWduaXNzaW0gaXBzdW0uIFV0IGFjIGxvcmVtIHNlZCB0dXJwaXMgaW1wZXJkaWV0IGVsZWlmZW5kIHNpdCBhbWV0IGlkIHNhcGllbi59ClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjV7XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMlxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjhcaGljaFxhZjgKTWFlY2VuYXMgbm9uIGxvcmVtIHF1aXMgdGVsbHVzIHBsYWNlcmF0IHZhcml1cy4gTnVsbGEgZmFjaWxpc2kuIEFlbmVhbiBjb25ndWUgZnJpbmdpbGxhIGp1c3RvIHV0IGFsaXF1YW0uIE1hdXJpcyBpZCBleCBlcmF0LiBOdW5jIHZ1bHB1dGF0ZSBuZXF1ZSB2aXRhZSBqdXN0byBmYWNpbGlzaXMsIG5vbiBjb25kaW1lbnR1bSBhbnRlIHNhZ2l0dGlzLiBNb3JiaSB2aXZlcnJhIHNlbXBlciBsb3JlbSBuZWMgbW9sZXN0aWUuIE1hZWNlbmFzIHRpbmNpZHVudCBlc3QgZWZmaWNpdHVyIGxpZ3VsYSBldWlzbW9kLCBzaXQgYW1ldCBvcm5hcmUgZXN0IHZ1bHB1dGF0ZS59ClxwYXIgXHNocHdyMlxzaHB3cmszXHNocGJ5cGFyYVxzaHBieWlnbm9yZVxzaHB0b3AwXHNocGJ4Y29sdW1uXHNocGJ4aWdub3JlXHNocGxlZnQyODE5XHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKe1wqXGZseW1haW5jbnQ1XGZseWFuY2hvcjBcZmx5Y250bnR9e1xzaHB7XCpcc2hwaW5zdFxzaHB3cjJcc2hwd3JrM1xzaHBieXBhcmFcc2hwYnlpZ25vcmVcc2hwdG9wMFxzaHBib3R0b20yNjYwXHNocGJ4Y29sdW1uXHNocGJ4aWdub3JlXHNocGxlZnQyODE5XHNocHJpZ2h0NjgxOXtcc3B7XHNuIHNoYXBlVHlwZX17XHN2IDc1fX17XHNwe1xzbiB3ekRlc2NyaXB0aW9ufXtcc3YgfX17XHNwe1xzbiB3ek5hbWV9e1xzdiB9fXtcc3B7XHNuIHBpYn17XHN2IHtccGljdFxwaWNzY2FsZXgxMDBccGljc2NhbGV5MTAwXHBpY2Nyb3BsMFxwaWNjcm9wcjBccGljY3JvcHQwXHBpY2Nyb3BiMFxwaWN3MjAwXHBpY2gxMzNccGljd2dvYWw0MDAwXHBpY2hnb2FsMjY2MFxqcGVnYmxpcApmZmQ4ZmZlMDAwMTA0YTQ2NDk0NjAwMDEwMTAxMDA0ODAwNDgwMDAwZmZlMjBjNTg0OTQzNDM1ZjUwNTI0ZjQ2NDk0YzQ1MDAwMTAxMDAwMDBjNDg0YzY5NmU2ZjAyMTAwMDAwNmQ2ZTc0NzI1MjQ3NDIyMDU4NTk1YTIwMDdjZQowMDAyMDAwOTAwMDYwMDMxMDAwMDYxNjM3MzcwNGQ1MzQ2NTQwMDAwMDAwMDQ5NDU0MzIwNzM1MjQ3NDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZjZkNjAwMDEwMDAwMDAwMGQzMmQ0ODUwMjAyMDAwMDAwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTE2MzcwNzI3NDAwMDAwMTUwMDAwMDAwMzM2NDY1NzM2MzAwMDAwMTg0MDAwMAowMDZjNzc3NDcwNzQwMDAwMDFmMDAwMDAwMDE0NjI2YjcwNzQwMDAwMDIwNDAwMDAwMDE0NzI1ODU5NWEwMDAwMDIxODAwMDAwMDE0Njc1ODU5NWEwMDAwMDIyYzAwMDAwMDE0NjI1ODU5NWEwMDAwMDI0MDAwMDAwMDE0NjQ2ZAo2ZTY0MDAwMDAyNTQwMDAwMDA3MDY0NmQ2NDY0MDAwMDAyYzQwMDAwMDA4ODc2NzU2NTY0MDAwMDAzNGMwMDAwMDA4Njc2Njk2NTc3MDAwMDAzZDQwMDAwMDAyNDZjNzU2ZDY5MDAwMDAzZjgwMDAwMDAxNDZkNjU2MTczMDAwMAowNDBjMDAwMDAwMjQ3NDY1NjM2ODAwMDAwNDMwMDAwMDAwMGM3MjU0NTI0MzAwMDAwNDNjMDAwMDA4MGM2NzU0NTI0MzAwMDAwNDNjMDAwMDA4MGM2MjU0NTI0MzAwMDAwNDNjMDAwMDA4MGM3NDY1Nzg3NDAwMDAwMDAwNDM2Zgo3MDc5NzI2OTY3Njg3NDIwMjg2MzI5MjAzMTM5MzkzODIwNDg2NTc3NmM2NTc0NzQyZDUwNjE2MzZiNjE3MjY0MjA0MzZmNmQ3MDYxNmU3OTAwMDA2NDY1NzM2MzAwMDAwMDAwMDAwMDAwMTI3MzUyNDc0MjIwNDk0NTQzMzYzMQozOTM2MzYyZDMyMmUzMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMjczNTI0NzQyMjA0OTQ1NDMzNjMxMzkzNjM2MmQzMjJlMzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDU4NTk1YTIwMDAwMDAwMDAwMDAwZjM1MTAwMDEwMDAwMDAwMTE2Y2M1ODU5NWEyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNTg1OQo1YTIwMDAwMDAwMDAwMDAwNmZhMjAwMDAzOGY1MDAwMDAzOTA1ODU5NWEyMDAwMDAwMDAwMDAwMDYyOTkwMDAwYjc4NTAwMDAxOGRhNTg1OTVhMjAwMDAwMDAwMDAwMDAyNGEwMDAwMDBmODQwMDAwYjZjZjY0NjU3MzYzMDAwMAowMDAwMDAwMDAwMTY0OTQ1NDMyMDY4NzQ3NDcwM2EyZjJmNzc3Nzc3MmU2OTY1NjMyZTYzNjgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTY0OTQ1NDMyMDY4NzQ3NDcwM2EyZjJmNzc3Nzc3MmU2OTY1NjMyZTYzNjgwMDAwMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2NDY1NzM2MzAwMDAwMDAwMDAwMDAwMmU0OTQ1NDMyMDM2MzEzOTM2MzYyZAozMjJlMzEyMDQ0NjU2NjYxNzU2Yzc0MjA1MjQ3NDIyMDYzNmY2YzZmNzU3MjIwNzM3MDYxNjM2NTIwMmQyMDczNTI0NzQyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDJlNDk0NTQzMjAzNjMxMzkzNjM2MmQzMjJlMzEyMDQ0NjU2Ngo2MTc1NmM3NDIwNTI0NzQyMjA2MzZmNmM2Zjc1NzIyMDczNzA2MTYzNjUyMDJkMjA3MzUyNDc0MjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjQ2NTczNjMwMDAwMDAwMDAwMDAwMDJjNTI2NQo2NjY1NzI2NTZlNjM2NTIwNTY2OTY1Nzc2OTZlNjcyMDQzNmY2ZTY0Njk3NDY5NmY2ZTIwNjk2ZTIwNDk0NTQzMzYzMTM5MzYzNjJkMzIyZTMxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDJjNTI2NTY2NjU3MjY1NmU2MzY1MjA1Ngo2OTY1Nzc2OTZlNjcyMDQzNmY2ZTY0Njk3NDY5NmY2ZTIwNjk2ZTIwNDk0NTQzMzYzMTM5MzYzNjJkMzIyZTMxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDc2Njk2NTc3MDAwMAowMDAwMDAxM2E0ZmUwMDE0NWYyZTAwMTBjZjE0MDAwM2VkY2MwMDA0MTMwYjAwMDM1YzllMDAwMDAwMDE1ODU5NWEyMDAwMDAwMDAwMDA0YzA5NTYwMDUwMDAwMDAwNTcxZmU3NmQ2NTYxNzMwMDAwMDAwMDAwMDAwMDAxMDAwMAowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyOGYwMDAwMDAwMjczNjk2NzIwMDAwMDAwMDA0MzUyNTQyMDYzNzU3Mjc2MDAwMDAwMDAwMDAwMDQwMDAwMDAwMDA1MDAwYTAwMGYwMDE0MDAxOTAwMWUwMDIzMDAyOAowMDJkMDAzMjAwMzcwMDNiMDA0MDAwNDUwMDRhMDA0ZjAwNTQwMDU5MDA1ZTAwNjMwMDY4MDA2ZDAwNzIwMDc3MDA3YzAwODEwMDg2MDA4YjAwOTAwMDk1MDA5YTAwOWYwMGE0MDBhOTAwYWUwMGIyMDBiNzAwYmMwMGMxMDBjNgowMGNiMDBkMDAwZDUwMGRiMDBlMDAwZTUwMGViMDBmMDAwZjYwMGZiMDEwMTAxMDcwMTBkMDExMzAxMTkwMTFmMDEyNTAxMmIwMTMyMDEzODAxM2UwMTQ1MDE0YzAxNTIwMTU5MDE2MDAxNjcwMTZlMDE3NTAxN2MwMTgzMDE4YgowMTkyMDE5YTAxYTEwMWE5MDFiMTAxYjkwMWMxMDFjOTAxZDEwMWQ5MDFlMTAxZTkwMWYyMDFmYTAyMDMwMjBjMDIxNDAyMWQwMjI2MDIyZjAyMzgwMjQxMDI0YjAyNTQwMjVkMDI2NzAyNzEwMjdhMDI4NDAyOGUwMjk4MDJhMgowMmFjMDJiNjAyYzEwMmNiMDJkNTAyZTAwMmViMDJmNTAzMDAwMzBiMDMxNjAzMjEwMzJkMDMzODAzNDMwMzRmMDM1YTAzNjYwMzcyMDM3ZTAzOGEwMzk2MDNhMjAzYWUwM2JhMDNjNzAzZDMwM2UwMDNlYzAzZjkwNDA2MDQxMwowNDIwMDQyZDA0M2IwNDQ4MDQ1NTA0NjMwNDcxMDQ3ZTA0OGMwNDlhMDRhODA0YjYwNGM0MDRkMzA0ZTEwNGYwMDRmZTA1MGQwNTFjMDUyYjA1M2EwNTQ5MDU1ODA1NjcwNTc3MDU4NjA1OTYwNWE2MDViNTA1YzUwNWQ1MDVlNQowNWY2MDYwNjA2MTYwNjI3MDYzNzA2NDgwNjU5MDY2YTA2N2IwNjhjMDY5ZDA2YWYwNmMwMDZkMTA2ZTMwNmY1MDcwNzA3MTkwNzJiMDczZDA3NGYwNzYxMDc3NDA3ODYwNzk5MDdhYzA3YmYwN2QyMDdlNTA3ZjgwODBiMDgxZgowODMyMDg0NjA4NWEwODZlMDg4MjA4OTYwOGFhMDhiZTA4ZDIwOGU3MDhmYjA5MTAwOTI1MDkzYTA5NGYwOTY0MDk3OTA5OGYwOWE0MDliYTA5Y2YwOWU1MDlmYjBhMTEwYTI3MGEzZDBhNTQwYTZhMGE4MTBhOTgwYWFlMGFjNQowYWRjMGFmMzBiMGIwYjIyMGIzOTBiNTEwYjY5MGI4MDBiOTgwYmIwMGJjODBiZTEwYmY5MGMxMjBjMmEwYzQzMGM1YzBjNzUwYzhlMGNhNzBjYzAwY2Q5MGNmMzBkMGQwZDI2MGQ0MDBkNWEwZDc0MGQ4ZTBkYTkwZGMzMGRkZQowZGY4MGUxMzBlMmUwZTQ5MGU2NDBlN2YwZTliMGViNjBlZDIwZWVlMGYwOTBmMjUwZjQxMGY1ZTBmN2EwZjk2MGZiMzBmY2YwZmVjMTAwOTEwMjYxMDQzMTA2MTEwN2UxMDliMTBiOTEwZDcxMGY1MTExMzExMzExMTRmMTE2ZAoxMThjMTFhYTExYzkxMWU4MTIwNzEyMjYxMjQ1MTI2NDEyODQxMmEzMTJjMzEyZTMxMzAzMTMyMzEzNDMxMzYzMTM4MzEzYTQxM2M1MTNlNTE0MDYxNDI3MTQ0OTE0NmExNDhiMTRhZDE0Y2UxNGYwMTUxMjE1MzQxNTU2MTU3OAoxNTliMTViZDE1ZTAxNjAzMTYyNjE2NDkxNjZjMTY4ZjE2YjIxNmQ2MTZmYTE3MWQxNzQxMTc2NTE3ODkxN2FlMTdkMjE3ZjcxODFiMTg0MDE4NjUxODhhMThhZjE4ZDUxOGZhMTkyMDE5NDUxOTZiMTk5MTE5YjcxOWRkMWEwNAoxYTJhMWE1MTFhNzcxYTllMWFjNTFhZWMxYjE0MWIzYjFiNjMxYjhhMWJiMjFiZGExYzAyMWMyYTFjNTIxYzdiMWNhMzFjY2MxY2Y1MWQxZTFkNDcxZDcwMWQ5OTFkYzMxZGVjMWUxNjFlNDAxZTZhMWU5NDFlYmUxZWU5MWYxMwoxZjNlMWY2OTFmOTQxZmJmMWZlYTIwMTUyMDQxMjA2YzIwOTgyMGM0MjBmMDIxMWMyMTQ4MjE3NTIxYTEyMWNlMjFmYjIyMjcyMjU1MjI4MjIyYWYyMmRkMjMwYTIzMzgyMzY2MjM5NDIzYzIyM2YwMjQxZjI0NGQyNDdjMjRhYgoyNGRhMjUwOTI1MzgyNTY4MjU5NzI1YzcyNWY3MjYyNzI2NTcyNjg3MjZiNzI2ZTgyNzE4Mjc0OTI3N2EyN2FiMjdkYzI4MGQyODNmMjg3MTI4YTIyOGQ0MjkwNjI5MzgyOTZiMjk5ZDI5ZDAyYTAyMmEzNTJhNjgyYTliMmFjZgoyYjAyMmIzNjJiNjkyYjlkMmJkMTJjMDUyYzM5MmM2ZTJjYTIyY2Q3MmQwYzJkNDEyZDc2MmRhYjJkZTEyZTE2MmU0YzJlODIyZWI3MmVlZTJmMjQyZjVhMmY5MTJmYzcyZmZlMzAzNTMwNmMzMGE0MzBkYjMxMTIzMTRhMzE4MgozMWJhMzFmMjMyMmEzMjYzMzI5YjMyZDQzMzBkMzM0NjMzN2YzM2I4MzNmMTM0MmIzNDY1MzQ5ZTM0ZDgzNTEzMzU0ZDM1ODczNWMyMzVmZDM2MzczNjcyMzZhZTM2ZTkzNzI0Mzc2MDM3OWMzN2Q3MzgxNDM4NTAzODhjMzhjOAozOTA1Mzk0MjM5N2YzOWJjMzlmOTNhMzYzYTc0M2FiMjNhZWYzYjJkM2I2YjNiYWEzYmU4M2MyNzNjNjUzY2E0M2NlMzNkMjIzZDYxM2RhMTNkZTAzZTIwM2U2MDNlYTAzZWUwM2YyMTNmNjEzZmEyM2ZlMjQwMjM0MDY0NDBhNgo0MGU3NDEyOTQxNmE0MWFjNDFlZTQyMzA0MjcyNDJiNTQyZjc0MzNhNDM3ZDQzYzA0NDAzNDQ0NzQ0OGE0NGNlNDUxMjQ1NTU0NTlhNDVkZTQ2MjI0NjY3NDZhYjQ2ZjA0NzM1NDc3YjQ3YzA0ODA1NDg0YjQ4OTE0OGQ3NDkxZAo0OTYzNDlhOTQ5ZjA0YTM3NGE3ZDRhYzQ0YjBjNGI1MzRiOWE0YmUyNGMyYTRjNzI0Y2JhNGQwMjRkNGE0ZDkzNGRkYzRlMjU0ZTZlNGViNzRmMDA0ZjQ5NGY5MzRmZGQ1MDI3NTA3MTUwYmI1MTA2NTE1MDUxOWI1MWU2NTIzMQo1MjdjNTJjNzUzMTM1MzVmNTNhYTUzZjY1NDQyNTQ4ZjU0ZGI1NTI4NTU3NTU1YzI1NjBmNTY1YzU2YTk1NmY3NTc0NDU3OTI1N2UwNTgyZjU4N2Q1OGNiNTkxYTU5Njk1OWI4NWEwNzVhNTY1YWE2NWFmNTViNDU1Yjk1NWJlNQo1YzM1NWM4NjVjZDY1ZDI3NWQ3ODVkYzk1ZTFhNWU2YzVlYmQ1ZjBmNWY2MTVmYjM2MDA1NjA1NzYwYWE2MGZjNjE0ZjYxYTI2MWY1NjI0OTYyOWM2MmYwNjM0MzYzOTc2M2ViNjQ0MDY0OTQ2NGU5NjUzZDY1OTI2NWU3NjYzZAo2NjkyNjZlODY3M2Q2NzkzNjdlOTY4M2Y2ODk2NjhlYzY5NDM2OTlhNjlmMTZhNDg2YTlmNmFmNzZiNGY2YmE3NmJmZjZjNTc2Y2FmNmQwODZkNjA2ZGI5NmUxMjZlNmI2ZWM0NmYxZTZmNzg2ZmQxNzAyYjcwODY3MGUwNzEzYQo3MTk1NzFmMDcyNGI3MmE2NzMwMTczNWQ3M2I4NzQxNDc0NzA3NGNjNzUyODc1ODU3NWUxNzYzZTc2OWI3NmY4Nzc1Njc3YjM3ODExNzg2ZTc4Y2M3OTJhNzk4OTc5ZTc3YTQ2N2FhNTdiMDQ3YjYzN2JjMjdjMjE3YzgxN2NlMQo3ZDQxN2RhMTdlMDE3ZTYyN2VjMjdmMjM3Zjg0N2ZlNTgwNDc4MGE4ODEwYTgxNmI4MWNkODIzMDgyOTI4MmY0ODM1NzgzYmE4NDFkODQ4MDg0ZTM4NTQ3ODVhYjg2MGU4NjcyODZkNzg3M2I4NzlmODgwNDg4Njk4OGNlODkzMwo4OTk5ODlmZThhNjQ4YWNhOGIzMDhiOTY4YmZjOGM2MzhjY2E4ZDMxOGQ5ODhkZmY4ZTY2OGVjZThmMzY4ZjllOTAwNjkwNmU5MGQ2OTEzZjkxYTg5MjExOTI3YTkyZTM5MzRkOTNiNjk0MjA5NDhhOTRmNDk1NWY5NWM5OTYzNAo5NjlmOTcwYTk3NzU5N2UwOTg0Yzk4Yjg5OTI0OTk5MDk5ZmM5YTY4OWFkNTliNDI5YmFmOWMxYzljODk5Y2Y3OWQ2NDlkZDI5ZTQwOWVhZTlmMWQ5ZjhiOWZmYWEwNjlhMGQ4YTE0N2ExYjZhMjI2YTI5NmEzMDZhMzc2YTNlNgphNDU2YTRjN2E1MzhhNWE5YTYxYWE2OGJhNmZkYTc2ZWE3ZTBhODUyYThjNGE5MzdhOWE5YWExY2FhOGZhYjAyYWI3NWFiZTlhYzVjYWNkMGFkNDRhZGI4YWUyZGFlYTFhZjE2YWY4YmIwMDBiMDc1YjBlYWIxNjBiMWQ2YjI0YgpiMmMyYjMzOGIzYWViNDI1YjQ5Y2I1MTNiNThhYjYwMWI2NzliNmYwYjc2OGI3ZTBiODU5YjhkMWI5NGFiOWMyYmEzYmJhYjViYjJlYmJhN2JjMjFiYzliYmQxNWJkOGZiZTBhYmU4NGJlZmZiZjdhYmZmNWMwNzBjMGVjYzE2NwpjMWUzYzI1ZmMyZGJjMzU4YzNkNGM0NTFjNGNlYzU0YmM1YzhjNjQ2YzZjM2M3NDFjN2JmYzgzZGM4YmNjOTNhYzliOWNhMzhjYWI3Y2IzNmNiYjZjYzM1Y2NiNWNkMzVjZGI1Y2UzNmNlYjZjZjM3Y2ZiOGQwMzlkMGJhZDEzYwpkMWJlZDIzZmQyYzFkMzQ0ZDNjNmQ0NDlkNGNiZDU0ZWQ1ZDFkNjU1ZDZkOGQ3NWNkN2UwZDg2NGQ4ZThkOTZjZDlmMWRhNzZkYWZiZGI4MGRjMDVkYzhhZGQxMGRkOTZkZTFjZGVhMmRmMjlkZmFmZTAzNmUwYmRlMTQ0ZTFjYwplMjUzZTJkYmUzNjNlM2ViZTQ3M2U0ZmNlNTg0ZTYwZGU2OTZlNzFmZTdhOWU4MzJlOGJjZTk0NmU5ZDBlYTViZWFlNWViNzBlYmZiZWM4NmVkMTFlZDljZWUyOGVlYjRlZjQwZWZjY2YwNThmMGU1ZjE3MmYxZmZmMjhjZjMxOQpmM2E3ZjQzNGY0YzJmNTUwZjVkZWY2NmRmNmZiZjc4YWY4MTlmOGE4ZjkzOGY5YzdmYTU3ZmFlN2ZiNzdmYzA3ZmM5OGZkMjlmZGJhZmU0YmZlZGNmZjZkZmZmZmZmZGIwMDQzMDAwNTA0MDQwNDA0MDMwNTA0MDQwNDA2MDUwNQowNjA4MGQwODA4MDcwNzA4MTAwYjBjMDkwZDEzMTAxNDEzMTIxMDEyMTIxNDE3MWQxOTE0MTYxYzE2MTIxMjFhMjMxYTFjMWUxZjIxMjEyMTE0MTkyNDI3MjQyMDI2MWQyMDIxMjBmZmRiMDA0MzAxMDUwNjA2MDgwNzA4MGYwOAowODBmMjAxNTEyMTUyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwZmZjMjAwMTEwODAwODUwMApjODAzMDExMTAwMDIxMTAxMDMxMTAxZmZjNDAwMWMwMDAwMDEwNTAxMDEwMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDAxMDMwNDA1MDYwNzA4ZmZjNDAwMWEwMTAwMDMwMTAxMDEwMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMgowMzAwMDQwNTA2ZmZkYTAwMGMwMzAxMDAwMjEwMDMxMDAwMDAwMWNlZjkwZmIwOWE3Mjk1MmQ2YTc1Y2JlYTRkNGU2M2M4ZmI1ZTQ2YjQ3YjdhYzEzZWVmYmJjNjAwZDFhYjAwNzYwNTZjZTAxMDA0MTZhY2FmY2Y3MmY3NjRjNQpiMmM4OGNiNzc3NWUzZGRhNzI2NGY1NzU0ZWQzMzYwZGIwMzYwMWFhMTEwMmQ2YzgxYmNmY2EzOTg0MTFjNTZjZTMyZDlkNDEwMWM4NDdiYjgzNGU4YmJjZWE4ODkxNmJlYWRkM2UzZGI2OTA3NTM0YjQ5ODkzODRiZDNjNmI1NwpiMWRjZmQwOTg0NmFmMWFiMzY2MDA0NGU1ODJkOTZjZTBiOGM5Nzc5ODJmNmYzZDM3YjUyNTcwMzZiOWZhYmQ0ZmQwZjBhNDU1OGZhNWU1YWNmM2E1ZDNlMmQ0ZWVjYmJjMzYwNGJkYmI5YTRkYjBlNjExODE4MzE1N2MxNjBmOAphYzVkNWEzNDdmMTVhNzQ0MTA3MjVkNjI2M2FmZTZlYWY0OGVlZjA5ZDcwNzQ5OTZhYTBhN2NlYTNlYjcwNWQ1MmM0ZTllNGY2OGUwYWY1NGE4M2IzMzI4OTQ3MmFlNTU4ZTQzMmM1YzM1MTRhZjg3NzYzNDNjZWQ2MjRkN2Y5ZgphM2QxYTI3YjJiNzk4NjE0ZmI5NDk4MzkxOGZjZGRiZTFmZDc0YzBlOWU0ZDc0ZGVlZGU3NWU0NjQ3NjQ0NTEzMjIyMTEwYjY0MGEwZDg0MmJjNWRlYjk0MGUwMTQ5Zjk3YjdkYWI4ZDM3NGYxOTg1YjNlOTQ1ZDgyMWEwOTU3YwphMzc2Zjk1ZmEzYzM1OWU1ZWQxZTdmNTc3MzAwNmU4ZTUxMzJiMTU2MjE2YzgxNjA3OTE3YjYwZDJhMjk0ZTcxODViOGRiZDUzY2RiYzkyNjI1NWU4YmRhZjFkNmMyYTYzNDdjODlmNTdjZjNlYTcyZTNkYjliNjkwZmJmNzk5ZAozNjhjZGQ5NWNhMjY1MTIxNjA4ZTA1NmYzNGU4YjUxNmE2MjgzNzY3NWVhNzk2ZmE1ZTY3YTRiOWZhNmNhNmY0MWZhN2Y5MWU3YmNjZjVmM2Q5ZjAzYmExNmQyN2MyZDUzMTJkMGIxOTdiMjkxZDY0NTI2NGU5ZDY1MmJhMDY1YQo2YWY2NmIyYTg5NGYzMGU4ZTljMTJkYWU5NGI4OTRlOGI5MmQ2N2NlZjRhZGNhYjYyNjdiM2Y2N2U3MjA4ZDJiYWQ3Mzk2Zjg0ZGFiYmNiNjEyNzc0MjRmNDlkNWEyZDg2OWE3NDA3NDg5OTIwMGQxODMxMTE5MjY5MWVhNDdhOQo1NDUzYTFlNmI3NWNhOTdlZGNkNjFlM2NjZjkzZWUzNmM4ZTAwZDA5ZDFiNGU4NWExMTc1NzFlYWEyNDhjODY1NmFiMmU1NTQ2N2QwNDRkYTMzYTMyNjkxNmJkMzZkMjkzY2YzYWVmMjFlOTAwMTQ3MTUzOGZjM2U4NTdkNTA2MQowMTUzNzlkODI5NDM2YzJlZmYwMDNlNzY5Yzg0MzkwNDQyZGEyNmQyMGQ2MDhlYWQ5MmNiZWIyMGE5YjIwYzRhYzkxOTkwOGNjZjIxZTdmYmNlNTI5ZDY1NTdhNzliYjZiZjBkMjA0NTFkY2FjZTU0Y2E5OTUzMjFjODFkOTMwOApiMWEyOGQ5YWFkYWVmM2QxNzU5ZjMxMDI0YWVlMTlkNzc5ZDc5OWVlOTk0YWQ0OWU3ZDI3NGE5MzcyYjY5OTZmNjRiZThiNzUzNTgwMjcxOWYwMTNhMjI1ODY2ZDlkNzMxMTE2MzIzMDllODJkOTMwNTQ2ODRkZjg0ZTFmNTVjYQo0NmM4MGM4OGE4YjJkOGNiMzE1OTcyMzMyOTE1Yjg4NmRhOTkxNGI2Y2ZiMmM2MWM2M2M1MGNjMGJlMzExNzgwYjM2NmM3NGFjZDk1Y2E4YjIxZTA1OTQ1ZDY0MmIzNjUzYzBiMDA2ZDE5MTM4ZDcxMGNlOGM4MTBjNjIyNDc2NgowNDgzMTYzMDk3YWY5Yjk5NzFhNzNkMmUwOGE4YjJjYjg0YTAxYjIzOTA3ODE2MDYzMmQ4NWI0NGMwNzZiMmJhZTRkOTk1ODA5OGYxNzM4MzM1NzJmNTFjZmYwMGZmYzQwMDI3MTAwMDAyMDIwMjAyMDAwNTA1MDEwMTAwMDAwMAowMDAwMDAwMTAyMDAwMzA0MTEwNTEyMTAxMzE0MTUyMTIwMjIyMzMwMzEwNjMyZmZkYTAwMDgwMTAxMDAwMTA1MDJhZjJkNTY5NWNiNWQyNjUyNmNlNWQ3YWY1MTU5N2FmMjJhMDk5OTZhYmJmMWFlYTE3OGM3MDczMzdmYTZlYgo3YTBiNzNlYTQyZjlmNmJjMmRiMzM4YmFiNTUzODhiYzZlMzQxYzdlMzRmNmZjNzllZGI4ZjNkYWIxNjdiNTYzNDZlMTMwY2NmNjVjNTU5OGJjN2Q3OGQ2N2U5ZTYyZGViNDI5ZDlkY2RjYWJlZTdjNDRlYjViNDFlMGNlYTgyZgplNWYxNjgxOTNmZThlYzIzODljZmY1Yjg3ZmIzOWJiYmYzZDY2NzY5Yjk4MmJkYjIyOTFhYThjMWUxOTk1MGJiMWVlYTRkNzYzODljMmU2ZmE0Y2YwZGIxZmE5Y2U5MzkwYjdjY2U0MDFkMGVkMTdlNGYxNjliNzFmMDBjMWUwYwozNjM5OGM2ZWIxYzdjM2VjMWUxNzNjNjVlMTZlNmZmNGU1Mzc1YWFmYTZjMTc3OWIxNWEyNGUyMTNmMTBmYTczMjkxNmQxOTRhNjhiOWRhNzBmOWRlOTMzOTFjM2FmZTljZjdkNTZjYTFhNWI4OGFkMmNhMWU5OTU2NDAzMzhmNAplOTQ3ZDJkZmNmZjQxODcxOGNkY2UwNzNmZDQ2MmZlOWNmYjNlZThjYzAwYmVkMTZiNTE4NGFkNzBiODU0YmVhODRmNTUzYjA5ZGQ2NzcxMGI0Y2RhNDVkNDY2ZDI2OGM5OWM2NjU5YzRjZGE2ZTRiMmJlZTI3NzEzYjRkY2Q4OQpiMTM3MTliZWNjZGI4YjY1ZjljMTA1YjkxNjVjZDhmNDc0OTg0Mzc3ZGNkZjk1NGY4MzU0MDhiMDE0OGQ3ZjkwYWZjYmRkNjRhMzNiMmFlNGU0YjFmYjVjMzhkNzY4Mzg2YjhjYzNjNmNmYzc1NmY3MDI0NTVjODk2YWU5NmVhNAo2YTdjMTgyODdmMzZjNTNkMmMzZDcxZjI3Y2M0NmQ1OTczNTU1MmEwZGNlM2ZmMDBlMDYzNjQzMTVjNmI4NGY0ZjZjMzY2ZTdjNDY0YWRhM2E1NmIyZmVkMjgyY2IxMjBlYjBkOWQ1MTcyMmQ4MzIwYzM5NjgwZmFjYThjMzk1NAo0MzliNTRmNWY1Y2I2ZmFlY2FjZDhiM2FkNmQwZTM1MjYxYzExMzA2ODYwODhlZTgxNmM0NmYwZGNkYzBlNTYxYjBjMmU2N2RkMmY2YjAyZDY0ZWM3NDlmNmNkODhmNWFiY2I1M2NiZjFkZWUzMjNiMGViOWNhNTU3MzM0YjRlNAo3NTA4YzEzOGRmZTdjMTlkMDRkMzg5Yjg0ZmNmNzEwZGMwNGFjYjVhM2E1OGEyZDYwNmIxYzk2MGFjMWNiNzE5M2RlMzhiOWVmNWM2NGY3YmMwOWVmNThmMDcyOTQ4M2VmMTZjYWY5MGU0NmQzOGViOTRjYjU2MzU3NGMyY2IzNwozZTNjMzUzNTM0NjBiMjc3OTY3ZjMxZjFiY2Y2NDM1MjJlNTUyZjkwMTMwZWFlOWU4NzE3NjMwZjFjNGY0ZjhmM2M4YTI3OTM0Y2YyNjk5ZTVkMDI3OTk4YzkxYjM1MDQ2Y2RjODMzZDQ2NmY5ODk5ZDVjNWJkMWU2Yzc4ZWU2ZQo2Y2NkZWMxNjk2MjA2OWQ2MDQzMTExYTAwYzIyNWI2MDgyZjU4YjYwMzM3MzdhOWU2MDllNjZlN2U0OWY5M2MzZTI3OTY4NjAzNjJjMTk1NjA4OTk2YTYzZDhlNWFhYjNlY2RmODFmOTlhODZhMDYwNGViMDZhMDlhOWQ2MGRhYwo1YmY1MDU4YWQzZTNjMzczYjQyNjc2OWI5YjNlMGM4MWE3NTc1OWU3NWNiMDM2ZmU5ZmVjZDZhMGZhMDg5ZjMxNmM2MTE2ZjlkODE5YTlhM2UzYjgwY2RjMzA5OTViMTgwZmQwMjA5YTgzZThkNDIyNmE3NjIyMmQ4NjA2ZGMzMwo1MzVlMDYxMzE5YTdmZmM0MDAyOTExMDAwMjAyMDEwMzAzMDQwMjAyMDMwMDAwMDAwMDAwMDAwMDAxMDIxMTAzMTAxMjEzMTQyMDIxMDQzMDMxNDEyMjMyNDA1MTQyNTI3MWZmZGEwMDA4MDEwMzAxMDEzZjAxZTc1YjQ1OTA1OQo1MGIyNDRlNThkOGU3MTMzNGQzNjdhNTZhOGY1NGVkMzJiZDljNThiNzdjOGIxMWI2MjhiMTIzZDVjYWJjMTA2M2Y1NzMzYWJjODc1OTkwZWI2Njc1OTIzYWM5N2Y0NzNhN2Y0NDczYTVmNDRiMmE2YmRhYzNlMTE2NTk2NjMzZAo0Y2FlNDQ1OGY0OGMxY2JlMDVlOTVmZjkwYTM4ZTNmMDhjYjE1ZmI0NDVkZDY1OTdkODhiMmNiM2UyMDY1NzczMTBmNGY0ZjNkYjIyNGE4OTExZmYwMDUxYzZiYjZjYjJjYjJjYjIyNTk2MzY0N2NiMzJiYTg4YzQzZDIyYzhjYgo3YzJjNjQ4ZmRkNTk1ZDk3YTVlOTY1OTEyZDliY2RjNjI0N2E5N2Y4OGZiNzA0ZTlkMTNmMGM5MzMxY2U5ZDBjNjMxZjdjNzRhMmE4YzUzNDY3N2U2YmJhMjRmZjAwMjg1OGM2NDY3YmEyM2VmYmQxMzEzMmNiYmYwNDUxMWM1Ywo5NmNlOTRlOTRkYWNkOGNkOGM1MTMxM2ZhMzIyYTYzMzFjYTk4ZmJkY2E4NGM0NTk3NjQ1NTExNjYxZjEwMmY0OGU0Njk5OGRhOTEyNDk5ZTJiYzExYjI2YWNlMzM4ZjU2Y2RjYzcyMTRhZjRjZGZhOThkZmUzYWExMzEwYjI0MQo3ZDljZDBmZWNlN2M3ZmQ4YjFkMWZmMDAwNGU0MjFjOTFiYzcyMWIxZjkxYzQ2YjQ3YTU4ZGQ4ODRjNDIxMTE2YmVjOTQxNGJlMDk2MzkyZDI4YTFjMTQ4NThkMGUyOGE0NjQ1ZTBiMWZiNTYyNjI5MGQ5ZTRkZTVjNWZjOTVhZQplMWU0OGFmOTM3YzVmZDkyNWUyODcxNmI0YTM2MzM2MzM4ZDljNjcxOGUyOTc3ZDk2NWEyOTBjNjRlN2I0NzZmZTQ4Y2Y2OWQ0NDhlYTI0NzM0OGU1OTFjOTIzOTI0NmY5MTcyMzZjOTljNzIzOGNkODM4YjJiYjI4YTFiZDE4Zgo0YTI4NmI0ZGM1ZWI0NTY5NDZkMjhhMzYyMzhjZDg2YzY2ZGQxZmJmNWE1Njk1YTIyOGRhNWU4ZmRkYjJmZDliZmU0ZmZmYzQwMDI3MTEwMDAyMDIwMTAzMDMwNTAxMDEwMTAxMDAwMDAwMDAwMDAwMDEwMjExMDMxMjEzMjExMAozMDMxMDQyMDIyNDE1MTMyNzE0MDYxZmZkYTAwMDgwMTAyMDEwMTNmMDE5ZmE2NmU2NGJkMzMyNWU5ZTQyZjRmMzM2MjQ5MTJjNTIzZDI2MzcxNDdhYzVmMjMwYWE1ZGE5Y2FiYzBkYjYzYjEyYWU5ODE1Y2FjOTIzNDliNjZkMgozNjRkOTM2NGE5MmZiMjU2ZmM5Y2Y2YTRhZDkyNTQ1MTQ0Zjg0NjA1NTAyNDIxMGU1YTRkNmRmODE2MzZmY2IzZjg3YTVmNzYzMTI2OGQyNTEzZTU5MDU1MTQ4OTExZTk5M2Y0OGNhYzRjY2IxYjU2NWY2YTg2MjU0ODY4ZDIzMgphZTVkMTg4NDQ5MTBlMWQwODQ2NDhlODk3NmU4NmI4YTFlMmZjMWM0OTk4ZDdjYWZhMzI4NWQyNmFiOTIxY2FiMTIzMmMzNTQ3YjcxNWNmNGIzODdlNGM5ODVhMjJiZDk0MzI0NjA5NTNhMTc0Y2QwZDJmYjUwNDMyOGFhMjcyZQoxOTI5NjllMGRjMWU0MzVhMzcxMWFkMGU0Nzg3NjYzNzZiYTY0OGVhNDU1N2JkMmIxYWEyMGI4MjhhYTI0NjVmMDY0NTZjNjg2ODk2MDU0M2UwNGFjYzc4MjM1NzI2NjQ4NDE3ODMxYzhkYzQ2ZWEyNTI4NDg1YTBmODFhNTBhMAozOGQ3NGM1ZmQxM2ZlODU1ZDE5NDY1ZjIzNGM3MDYzODQ4Nzk1YjNmZDM4MzUxMWZmNDkyZmYwMGQyOGExNzA1OTY3MjJiMzkzNDFiNmNkMmZlY2QyNzI2YTY2YjMyNzNlMDhiNjg1MjQ3MDU5NjU5N2Q3MTc5MmJkODk4YmQ5Ngo3YzA3NWZhM2ExOGQ1M2U5NWVjYTM0OWExOWEyNDQ3ODYyOGQ5YjRjZGIzNDIzNGM0ZjgxYWEzZmE1YzA1NGZjMTVkMWY1YTFhMzQ4ZTJmYTIxMGEzNjIxYWQ0NmNjNGQ5OGZlOWIzMWZkMzY2MWZhNmQ0M2Y0ZDk4MWI1OGNkYgpjNjhiYzY4ZGU4OGYzOWJhOWZkOGE2NmIyZDc2MTMyY2Q0NmEyYzY5MWEwYWFlYjQ1MWMwZWJhZWE2ODU5MTliODZiMTY0OGZkOGU0YmViZGE5OWU3YWQ5Njc5MzQ5NWRhYjM1MTY1N2JhZmRiN2QyOTBlM2RhYTFhZmY4MWM3YgoxNDdmZmZjNDAwMzcxMDAwMDEwMjAzMDQwODAzMDUwOTAxMDAwMDAwMDAwMDAwMDEwMDAyMDMxMTIxMTAyMjMxMzIwNDEyMTMyMDMwNDE1MTcxMDU2MTgxMjMzMzkxOTJhMTM0NDA0NDUwNjI3MjgyYjFkMWUxZmZkYTAwMDgwMQowMTAwMDYzZjAyY2E2NjU2NTJiMDM2NjI4ZGU0NmFiMTUxMjQ3ODU0NTU3NmIxNTc0ZWE4YjViZjM1OTkxNjQ1OTE2NDU5MDJjODE3YmIwYWViNjQ4Yjk4MmE3ODUyMDcxM2JiZjRkYzliOTY3OTlmMjQ3NjBjOTc3NDFlNzM3MwplMmI2MWY0YWVlMzAyMWU3NWRjNzM3YTg0ZTZiYWNkNDcxZjY3MTM4ODU0NGYyYTZlMzlkZDAyOTZlZWQwMGIyNjMxMDliMzM3ZGI0M2MzMjlkMTA3MzMzNTdhOTY4Nzc1MzNkZTJkNGU4NjZjMTMzNzFmNDI4MTFjMjIyZDk4YwoxMDA1MzQ3NDZlZmVkOTgyZDBjNzFiY2RhNzA4MzZjYWFkOTM1NDI2Y2I5YTk3NWFlZTYzNmI5YTUzOTk2MzVkM2JhNjg1MDcwM2MwOWE3MDFjOTU0YWQ1NjI5OWM1NmI3MjY4OWEyM2E1MzcwOGFhMmY3MmY2NTA4YTk0NzY2YQoyOWI0NGQ2NTJhOGQyODBhYjgyYTQzNDMwMDE1ZWM2ZGQ2ZDZhNWFlNzZjY2Q1NTY4MmQ3M2JjZDRmNjY1NjQyYjIxNThhYWFhYjY2YTkwZmU4YTkwZGM3YjI5MWQwZGNhYjA2NGIwNTQwYWFjMGIyYTkyYWFjY2E4NmM5YjVmNQoxYzk1NTY0MDU2MTI1NzYyN2M1NmE4YTk2YTkzZGIzZGNjNTYzNjYyYjMyMTIyN2QxNDhjZmUzYjk1NTU2NGM3NTBiYTg1ZmVkMGFlYWE4ZDNlYWFlNDM3OTFkOTRlMjY4MmYxZTZkYTJkNjkxMWU0ZjBiNWNjYmQxM2EyYzhjOApkMmRhMWIzYzk1NTc1NTcxODRhOTk2NDgyMjQxNTI3YzZkNGVlZDJiZWQ3ZjA2MTVlZmUyMWVkMDhhZmM0M2JiNDM1Nzc0NWQzMWRmYzE1M2MzNzRiM2U4YTcwZmMwZTJjZmQwMmJiZTA2ZmYwMDlkNGExNzgxZmFiOWNhN2E0YgoyMTQzZmQyY2FhYmJhZTdmN2JjYmJmYjU1OTJhZjAwYTljNThhMThjZWY1MjgzNTg1YTAyMDE5YTRlY2MwNDM2OTEyNmVlYTE3MjJiOTJlNGI5NWI4YWZmMDA4YWVjMzczOTVjODIwNzc1Mzg4ZGQ3NmY0MDY0YWYzNWQwZmI4NQo0NzAzYzE5ODUyNTk4YWM0YWEyYTk5YWFkMTUzNzJlYWU0YWI2Y2MyYmIxMGZhYWJjMjZhYjQ1MzZiOTVlYzc4NzQ1N2FkYzM4Mzc1ZGY3MGFmZTQ5ZmZmYzQwMDI4MTAwMDAzMDAwMTAzMDMwNDAyMDMwMTAxMDAwMDAwMDAwMAowMDAxMTEyMTMxNDE2MTEwNTE3MTIwODE5MWExYjFjMWQxZTFmMDMwZjFmZmRhMDAwODAxMDEwMDAxM2YyMTM1MGI2N2YxMTk0NmFmNjI5ZTU4OWNiMGQwODFmNGFjYzkxNDg0MGNkMDM2MzYzZjUzNTQ5YTM3YjllZDA3NDJkOQplMGM3NTliYWM2ZDNjMWExNzVhZGY4ZThhMmI0N2MxZDkwZTMwZGJiM2UwZmZjMDFjYzM1YmY4OGZkMDQzNzY2ZTliMGQ4ZGZhZjRlMGUwNmY4Mjg1ZDJiM2YyMjMwMmQxMjJiZDI4YmIyMmU0NzlhOGFkYjIxMTljMzY1NmVjYQo3MGJkOThkOGZhMzFiMmZhOWI4YTk3YWI0NTE4YWY0ZTkzY2ExNTljY2ZkMDZmMTg1NzJjMzIzNDcyZTVjMmY4NjIxNmQzMjk0NmM2ZmQ3NzQ1YTI3NTMwOWEyYTYzMDc4MTA1ZWU2MjI2ZDhlODRlYTFmNmIwODZlNjY1OGNhOQpmYTAwODRiZDZmYTZhZjgxOTgzNmRjYzI5YzJkMDk3OTRlOGY4MzIyNWZhY2I0ZmIxOGEyNDJlOGY1MmI4MzA0MzFlMGNjMzE3ODMwMWE1NWE4YmZmMWU1NmMxNWFhMTZiYzE5YTcwMzRiMGNiMmFjYWFmOTYyNjUyMTNhMmQwYwo0YjY5YWY0OWIyNzU2YThlZWU3ODRjNWU5YTUyOTZhMzkyOGMyZGEwYjI5Yjc1M2IyNDY4Yjk3OWM5ZjhmZDBiYmM0ZjcyNWFmNDk1ZDE4OGIwNGQ1NmExY2E1OGI4MWIzNWYwNDEyNjVhNjhlNTM5NDhlZTQxY2M1YjcxYTI1NQo4ODRjMTgwNjMwMWQxMmQ4N2Y3MTdiYWVlMzIzNTk0MWI5OWZkMDU4NGYwMzJhYTNhNDUwZDYxNzhkODQ3NzE1ZWIwODEzNjg1Yjk4YmY3M2Y4Y2M2YTE5OTE0ZDliMTk3NTM1ZGQ5NGVlZTQ1MTZhODRlYjQ0YjBhYjdiMTJlYwo3OTM3OWM4YWYyZGFiNTIzNzZmNjhkMjA3MTEyNDM1YmQxZGU2NGE0NTVlNGQ2MDkwOWIyMGY4MThhMzEwZDAxZjYwOGY3YzIyMTdiYzkzNDg2YmY5NjcwNTA4NzQxYjE2YmYxODZmNTkyOTE4NjVkMTkxOTUwMjhiMmIxZTliYQpjODYxYjA3NDBiM2NhMWJmOTRiYjlhN2I3OGIxY2RhZTEwNWFjNDFiNmQ3NzYyNWMxNWJhMzRhODYzNjg2OGEzYTQxMTQ1NWU0Njc3ZTg0ZGJlNDU4MDJhMWFjZDcyMzE3ZjYyN2RhOGUxNDY4ZWNmNzFlOTdlYWVmMjNjYWIxNgphMjY5ZTRiNWNhOWNkYjAwN2EwOTc4MzU5ZjkzNTEzNWIyN2VjNWUxNjk5MjFlNDAzNzBhNDk3MjE3YmEyOTA2NWNhOTRkNmI1MGU3NGRlYzc0MTRjZDA2MmZkNDM1ZWE5ZTRkMjlhMGNjZDQyZDcwMzZhZjIzNTZjY2JlMjVjMAplMzJjYjZmZDA0MjNhZmY4ZjgxMjRjMjdhN2JjMDk5ZjQyNmJmNzMyNmEyYTViOTBhYmFkYmVlZDNmODI2YzJlMDI1ZjI2NTA3ZWUzN2M5NmRiMzVlZmY5MDBhYzRkNWIxYzFkMDkyZDg4N2FhMzhiMTg1Mzg3YmExYTc3MWVlNgphOWZkMTg1OTE4MTU1OGU4OTMxNzFiM2EyNTZiMTI1NzcyODI0Y2FkMzZjZTYxYTFhZjhhMzNkZDVlYzdmOTQxMmZiN2QxZTFmOTJhYWQ3ZTRlMjdiNTE4NWYxNGExZjdkMjFlNGMxZTk1MjcxZmViMjJlZjhmNjI3OTU2Y2M1NwpiOTI5Y2E0MDljYjBmMjhiZDRjOGY1ODdkMDQ3MTkxMTQ1M2YyMjkzMzRlNDgwZDhjNmMxYzljODBkNDc0NGUyODYwNzc4ZDBmNDEzZTQ2OTk2ODEyNGRhNTdiOGRiNmEzNjIxMWY3NTgzNjMxZGIyMzE2OWYwM2I4MWM4Yzk4YQozMjY4NTIyYjc5NGYyNzc4NGJiMTkxMjg5YjFkYTQ0MTkwYzVmNWNkMTIxYTczMjM3NjY0NDgyOTIwY2ZhMGQyZTgzNDdhMWM4MjdkY2UwMGRjMjNkNmU0OTA0Yzc5MzQyOTIwNGZhM2ExNTEzN2I5MjhkMjIzYzFjYjg4NzhjMwphMzc1YTMxYTg3Y2ExNDFmNTA3ZWIyYjc0NmJhMzA5NDg1ZDQ4NTkyMDQwYzJkMGJlOWQ4ODQzMWE4YzIxODc4ZTQ4ZmZmZGEwMDBjMDMwMTAwMDIwMDAzMDAwMDAwMTA2YjA2MzFhOGY2ZTQ0ZTBjNzZjZmM2YTE5N2MzNGI5YwpkNWE1ZTBmYjU0MDM4NDEyZGFiZjRkMjAwZGE4ZWJjZjY2ZWEyNjQyMDcwZTJlY2I5ZTZlZjNkYWFmNDZjNjQxODRiNzA0OTAwZTMyZmQ0ZWQxMzNjZDVkNmQ4MGI4MTI0MTJjYmU4MDlhZTE5OGU3NzEwMDk0OTIzM2ZmMDAyZQoyMGRkYjIzMzk4YzliMDkxN2NiNWRhNGZlMTgyYmQ2Yjk0ZjFkNWFiMTAwMzUxMjg3Yzc5MDJlNWI2ODEzOTE0NGRkOGRjM2UzZGZiYzdlODE5OWQwMDk4Y2Q4MTlmY2Q5Y2RkNDhhYjIyNDkyODBlNzgyZmMxOGZmOTUyN2E5NgoyOWE0MjkyMjEwZjQ2NWNjNTI3MTVhYmEwZWEzOWM2ZWJmZmZjNDAwMjcxMTAxMDEwMTAwMDIwMjAyMDEwMzA0MDMwMDAwMDAwMDAwMDEwMDExMjEzMTEwNDE1MTYxMjA3MTkxZjAzMDgxYjFkMWMxZTFmMWZmZGEwMDA4MDEwMwowMTAxM2YxMDQzODljYzIxYzk2NmJiOGI2ZGRhY2RiYjMwZTZlMTc2MDMxNzY4MjA4M2YyMjZiZDIyMGMzODIzYWE0ZWFkMzhiMDNmYTBmZjAwOWYwODNhMDlmYWJjMWZhMzdkNjVhNzA4YmRjMzc0MTg5ODY3YmIzOTgyMGZjYgoyMWNmZTJjNjcyZTU5MDBmZDVmMTM5NjZmMjEzOTc5MGJkOGZkY2UwMmUxODQ0NDFlMzdmMDAzZTAzNTllMDE3MDJlNGNlOWM4ZTY2ZDBmYWUzYzVjZDkzYmVlNTQ4YzI2NmFmYTdmY2RhMzE4MjBiYTk2N2YwMDIxMTZiMWNhZQo1YzQwMWUyODE5MGQ3NWYwNTM2MGRmNjgyY2U1YmRkMjUwNGY3MWUwYjJjY2RmMGQ4ODQ3Y2MwM2E0NzJlNjc1N2I5YjFjMWZhN2VmZmYwMDUwYjI1OTZkYjk5NzRjZDYyZTRiOWI3NGRjNjUyOTRiMmRiNmMzMGNhMWIyY2JlNAo0YzBiNzExMmM5NmQ4OWUzNzA4ZjY0ZTc5NjUzZWU1MmNiMmNiNmRiMTA3Y2MxMWRiMTY2OTkyYWZlY2ZmM2Y3OTU2YTFiYWJlYmIxZWM5MzM2ZjZhZGYyNTcxY2Y0Y2E2NjU5OTZkODA2YjZjNjkzZTIzMTZmODFlMjdiMmUyNwpkZjNmYmM0NjEwYzI4YzQ2MjVjMzgyZjYyNjc4Y2ZjZTRjMDg2MzJkOTFjNzgxNGVlZDUxNmJhNWIwODc4OGQ2NzllMGU2NjdjYzQ2NDA3ZDJjM2Q2ZWMyNDY5MDVjNmNiNGQ1ODQ3NTIxZjU2OTBjMDUwN2NjMDdiOWM4MTY2MwphMTBjYjA2MjRiM2FmMDI5NGEyMjQ1NzVlMzc3NzNiZWU3OWU0NjNkNDg2YTQxZjU3YzM5ZjhlZWQ5MmI2NGNiMmRiNmNiZTQ2MTIzZWRlNTA1MzIxMWQ0MmU5OGU4MmRhYzY2NGI5MzhiYjg2NWJiMGU1MjJhNzcxMjM2ZGZlMwpmZWFkM2ZmMWZmMDA1N2YyZTdjMWFiNTc2OGNlN2FmMWIxNmMzMGFmYjQ1M2U0N2FjMWFjZDM2Nzc3Mzk5ZDM4MjUzZDRiZmE5NWY1MzZjMjk2ZGVhZjg0YzI3NjQ3Y2RiMjFjNDJlY2YwMzYxODYzNTZiYzBiYjJiOTRlNDg5MQplYmMwNjk3ZGFjMzI1OWI2ODg1ZTAwZjg2YWNiZGRhZGY1NmQ5MDEwOWVlNTk0YjJjY2NjYzkyNTk5NmRiZTBiM2MwMmQ0MWYzNjIwYjg0MDYwNzgyY2E2ZDk5OTY2N2M2NTllMDYyMTM0ZjIzMTExMGMyYjY1OTk5OTY2ZGZlOAowYzMwZGJlMDYxODZkYmZmYzQwMDI1MTEwMTAxMDEwMDAyMDMwMDAxMDQwMjAzMDAwMDAwMDAwMDAxMDAxMTEwMzEyMTQxNTFhMTIwNjE4MTkxYjFkMWMxZTFmMWZmZGEwMDA4MDEwMjAxMDEzZjEwZWVkZTA5MGY4NmNkZTE4OApmNTIyOTkzZGNjOGU2OTJiYjljMTZkZjEyY2YxOTY1OTI3MGZlMzNjODc3MTVlMTc4NDA5ZTZkYWY4MWZmNWMwNmJiNjAzNmQwM2QzNmJlY2ZiNmM3NDYwZmJhYzU4NzBmMTk2NTkyNDkyNzA1ODI3NGM3MDliOGZkZmY4OGM2MQoxZjZiYTY2NGU4ZDU4NTk5ZTcyY2IyYzkyNDliNGYzZmEyMGUwNWZkOTlmZGZmMDBkNDIxMTBjZjA4MDA5YzVlM2ZkOTFiMmRiNzgwYjJjYjJjOTI2MWYxYzI2Y2NlMDhlMTE4ODk3Y2YwMWMzNzI3ZTQ1ZTJkOWNmMzJhMWU5Ygo2MjA4MjBiMmNiMmM5Mjc0ODQyZTkyNDYzYzMwYmY4NmUxNDQxMjVhMzFlYWMxMGUyZWJiYjIwZmIxMTA0MTY3MTk2NDk2OTc3MmVkN2E0NWI4OTY0MmZkZTE4MzkwZTk3OTBmMDBiY2U5ZDMwNzAxMDcxOTY1OTI1ZGVjMmQzZAo0NjVhY2EyNmM0MGZlNTI2MWIyZTE1YTBkYzlmMDBiMDk4MjM3MjUyYzYyMTg2MzhjMjU1ODRjYjFiMTMzYmIxMTZjM2E3ZGI2Nzk0ZGZmM2U2MWFjYjY3ODkzMTJiZTdiMDA3OTZjYmRkOWY2ZjFjMDllNTg2ZTlkNDFlYWM1YgozZDY1ZTFlNTExNTM0Nzg2NzgzM2EzZjY5Y2VlNWYwY2FmNTc1YWNjZmQ5NmJhZGJjYmI4MmQ1OWQzYjBlNmMxYzNmOTBmYTEyZjk5MTRhNjBkZDFiYzkzNWVhZDFlZWNlZGZiMjZiNWY2YmMwMjZkZjBjMmI4MmExOTY5ODU4NQo5MGU1OTE5YTU5MTEyMTNkZTk4ODg4MDc2Y2FiYjYwM2E4YjU5OWZkNWU0ZTEzNjE5MzkzMWQzYWJjNzAyNjE0M2Y0NmM5MWE5M2MxOTdhZjAxZjZmZjAwOGZmNzFlZTdmMjU4NzZmZTRiMDdiM2ZiYmMzOGZlNTY1ZWNlNTM4NAo4OTVmNzc4OTgyY2ZjNWVhNThjMmY1OGJjYWMxZTBiYWNmNTFmNjhmYjIyZDhjM2ZmNzVmYmJmOTgyZjdmOWI1NmFmZTYzYWY5MjdkMzBmYTllYzQzZjRkZmJhZDI3MWU5OTk3MjVkOGIzMjBkOWNlZTE3NjM2YTdlZTBiZGNmZgowMDM2YmQ0YmVkYzZjNmE3MWRmMDVlOWM5ZDQ2ZjYyM2VhMmU0YzE2Yzg0NDc4ZWI4NTJkOGVjNmM0MmE5ZGE3ZTI1MWRmMWJmYTMyNmNlMDY0MmY3NjI3MTY0NWJjMWUyMzVjNzhiM2U0M2Y2MjY4Y2I0YTQ4OWM2ZmU4NzhjZQowNTI2NzAzMWM4ZjFiY2ViMGMzNjBjMTI3MWI2ZjE5MDQ0ZmZjNDAwMjYxMDAxMDAwMjAyMDEwMzA1MDAwMjAzMDAwMDAwMDAwMDAwMDEwMDExMjEzMTQxNTE2MTcxMTA4MTkxYTFiMWMxZTFkMWYwZjFmZmRhMDAwODAxMDEwMAowMTNmMTA2YTczMDgxZDY1MjA2ZDc1NGY5ZTUyOGY2YTMxYWE4YTI0MTdkMjUwNDllZjBjNTQxNTBjZjc2MGQ5YjhkM2RhNjlmMTJmZjQ5N2ExMDgzMDYxYjFkYzRlYTJhMWJhYmRjN2Q2MGUxZTVmNzhiOTY5OTU1YmI4YzE3YwphY2IyYjkxZjEzNGZkMjFlNzEwMDE2ZmIyMDBhM2UzODUzODNmNjliM2Y4NTE3YjUzZjY0N2IwYTFlODRiMGI4YjA1OTkwMWE5YjBiMmU4MTdlYTBiZWE0MjBmNThhNGMzODIzNDg2ZGZjOGY3YjZiNjU2MTFjMTZlMjgyYWQ0MQoxOTk0MDc3NmEzZmIyNWEzMDYyMThlOGE3OTU1NDExYTdiYTFhMDZjYTZkN2YwNGQxOGQ0ZTA4NTJhYzY4OGJkMDdkNDQzZDJlN2EwODA3NWFjMGYyZDFmOGNhMmNkNDZkNDVjYjIwZWIwZWEyY2Y4YzY3Zjg5NjI3OTE3YmViZQphYTBiMTg3MDQyNTg1ZDIxODZhNWRjMmMwNWU2NTJjMjg5NzFjMTUyZTM4NTg2MTExMzY0NjVmNDE5MjE5OTk5OTgzMmUwYzJiOWFjNGM3YTQ5Y2VhYmZiYjk1MGI4ZmI2Mjg3NDk0NjE3NDc5OTYzZjI1NzQ4MDBmNjhhYzk1NAowOTc4M2E2MDVmMDM5MDcxMTIzOTk0MjlhODViMTI1NjVhNjE1ZTZjZTYzMGMzNmY0MWE5NWE4YjJlNWMxOTRhM2E2ZmQ0MTQwMDBmNTViOWJiMGIxOTgxNjEzYWNjNjVkYjI5ZGU4NmY2N2Y4M2VmZDE2YzRhNDgyMjA5NTBmNQphNTU0MDk5YjU5NzI0NTUzNzEyOThjMTc4MWUxODAwNDAzNjczMGJjMWJmNTU5NzJlNWNiOTQ0M2E3ZGU1NjA5ODg0MWNjNWQ3OTkxMWU5MWY0ZTZhY2M1YWEwM2Y5MGZlN2E1NGE4NDYxMTgyZDg5YjY1Y2U4NmM4OGFjMzBjOAo5NjI3MTBhYzQyYTU3MjljM2U4NjkyZTJlMjJjYmY1MWEwYjBjY2Q5NTQyY2FkYzBjNDAzNmNjYTQyYTQ0YTdjNGJjZDZjMWI3ZWFlNTQ2MGNmNmI4MWYxNmY3OWMyOGMxODg3MGQ1NDZiZDIzYWNkY2NiMTMzMDdjNTlhYjk0Mgo0MmRmMThjYjJmMTgyMGRlMjllNjE4NjEyMjMwM2RjMDljNjViYzIzZDY5NDcxODMyODJiMmY0NTEwODRlMmE1ZjhhOWY3NmUyYjM1ZDZhZTM4YjRkNWUwNGFmY2U2NTViNjFhZTUxM2YwZmQ5NjdlNTU3NWQ0NTNmNjMxNTE2ZgphMDZlMmJhOGU0MDVlMjk4ZTgzNGJlNjA3NThkMTFiODUzMTBlMzcxMGIyYTEwNmQ0OGE4YjVjZTUwOTEwMTc4MjA4NTBiZDU4NGFmMzdhMDM4MjUwYzllNTk1OTZmNzI2ZWE2OGY4Y2U2NmRkNjU2MDU4YTc0OTdiMzZkNDA2ZQoxYTQ1YTk1NzI4MjI2YmQ3MjFjYzFjNDE1ZDhhNTgyNTA1OTMwMmIwMjJlMzc2MWQwMzg3YmM1MmUyNTk0YWI1OGI1MTUzMWE5NTMwZjc4ZDRhMDgyMzJjMTVmYjkxMTAxNzQ0YjIxOGI3ODg1NDBmN2ExODFlNjhmNTBmOTYxYgpiZjY2MjYyMWZhZDE2MmJiODI3NTQ0YzVhZTUyMWYxYTFhNWVhMDkyZDRhNGVhNDcxNDMxZDI1OWU5ZjgwY2M0MTUxNzJkNmExNmU1MDFhNTk3M2YzYTk5MmI3ODA0YjQ1NDY1NzBhMmUyY2ZkOTBjZTFlNGUyYzRiZjhiOWE5Nwo4YWFkZjc4NWRkNmJiMTI5ODVmNDVlMjE1MzVmMmMzYzA4ZjBjNzc1OGY5NGUyYmYxMmVkNTBlZTEzMWM1NWQ4OTdlMzA4YmIxZmQ0YjZmY2Y2NTUyODE3NmJkZTA3YWY5YTVlNTAxZWQwY2UwNjhlMTIyY2VlZDlmODM2NzlkNAo2YjA0ODQwOTRlY2RlNjIxNTMwMGFkNDFkN2ZhYzRjOTMzYjY3ODFiOGE0MmYyMTdlMDQ2MjU4YWFjZGJlNDk2ZTIwYmIxYjY3MmQzODhlNjIwNTQwMDFjZGE5NWRlNTE1MTE1NThlMzc0ODY2NWNhOGNmNDU4MTk2OWNlZDRmNgo5NjU5MGY3OGE2ZDU3OTJlM2ZjMTE5NDY4YWZhODliOWQ4YjY0OGJiNjc0ZGEwMGQ5NWUwYjhhYjg2OTE4OGY0YmQ0MGZkZjY4MDAzZTYxZDM0NjBhMmI1OWE4YWQxOGNhNmZkOTMwY2NiZGM3ZTg4YTYzYzk1ZmNjYjc0YmMwYgpmNjE1NjQ3NWMyY2Q0ODk5MWIxNzViOTBlZGJhZGI3MWUwMDgxNTgxYzE1NzQyNzk0MTA5MzQxYjA1ZTVkMDdiMGNiZTk3YjVmZjAwYjU5NWVkNTM3MjJlMDk3OThiYzYzZjAwOTBiMTgwZjYyNTBkMDhlODJjYjA2NDNjZTY3MApkMThjYjdjMjljYzIzYjEyYjY1N2RlMzk3NThlMzQ0YzgxMDNhMDcxZTYxNzc1YTFmZjAwZDU5OWE0NDAxZGQzNmU0ZWQwNzAwOTVmMTEzMzUwZjEyNzViZjkyYTkxNTZiZmU3MjhiMWVmN2ZjNDFmNDdiN2ZhNGNhMDFlZjdmZQoxMDBjZmUxMmZmMDA2ZGE3NWY4ODk3MGI0ZWEyZmM4NTJhNGU4OGZiODZhNzQ2NmQ2MjJiZTUyMWJlNWFiOTg1MjNhYzdmMGIyMTY0OGVkNGU1MmE1OTFjZDJkODA1Zjk5NjRjYThlMGI4M2U1MDNkNzk5NjZmNTBkYzBiZDI2ZgphODNkMWE5Njg4NWVjNmY4OTc4YjhiMTY5MTIyNDhkNmIxMTIyYWJjNDM0ZGZkMDk1YTQ5OGU4YzU0ZWU4NjRhM2JkNDU3YjExNmYwOWVkMmZiMjFjNTRhYjE2ODM1MGZmMTJiMjdjZWJlYTUzZDgxMDUxMDQzOTc1MzAwYmI1MQo5OTkzNDUwOTJjODRmMjgzMzBjNzU4NzY0MzVlYjgyNjBiYzAxZTYwNWMyZTE4MzA0NGI4YTYwNDU4YzU3MmU2M2MxM2RhMzUzZGM0MDMwOTdjNGE3NjEwNzg4M2MxMDIxYTIyZTY2MTE3OTA5OTI3NTNjODk2ZTQ0Y2Y1YWJhOQoxZmEyMTYwOTQ5MDQyMzZmMzE4NDZlNjJiYTg2NTcwNGU0ODljOTEwYjUxMjk5YzRhZjUzNDg0NThjNDQ5ODg1ZjI0NjUwNWI0MjUxZmM4ODFiMDkwNzM4Nzc4MjEwYzQxMDcwZDQ0YmM0NWU2Mzg2NDgzZDY3NzI2MzE1MWMxNgo0MWVkMGViMTBjMzY0NjZhMGVkMTI1OTE2NmEwMTMwOTRkODhhNmM4M2UyMTBjNGIyYzZlMTJlNjcwNDA5OTI1ODU4MjY0NjJhM2FjNWE4NGI4OTUyN2ZmZDl9fX19fQoKXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNVxydGxjaCBcbHRyY2hcbG9jaAoKXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNVxydGxjaCBcbHRyY2hcbG9jaAoKXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNVxydGxjaCBcbHRyY2hcbG9jaAoKXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNVxydGxjaCBcbHRyY2hcbG9jaAoKXHBhciBccGFyZFxwbGFpbiBcczJccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IyMDBcc2ExMjBca2VlcG5cbHRycGFyXGNmMTdcYlxkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMzMlxhbGFuZzEwODFcYWJcbG9jaFxmNFxmczMyXGxhbmcxMDMze1xsaXN0dGV4dFxwYXJkXHBsYWluIH1caWx2bDFcbHMxIFxsaTkzNlxyaTBcbGluOTM2XHJpbjBcZmktNTc2XHJ0bGNoIFxsdHJjaFxsb2NoCgpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1e3tcKlxia21rc3RhcnQgX19EZGVMaW5rX18xMDlfNzM2NzgxODQwfVxzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTJccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY4XGhpY2hcYWY4e1wqXGJrbWtlbmQgX19EZGVMaW5rX18xMDlfNzM2NzgxODQwfQpNYWVjZW5hcyBub24gbG9yZW0gcXVpcyB0ZWxsdXMgcGxhY2VyYXQgdmFyaXVzLiBOdWxsYSBmYWNpbGlzaS4gQWVuZWFuIGNvbmd1ZSBmcmluZ2lsbGEganVzdG8gdXQgYWxpcXVhbS4gTWF1cmlzIGlkIGV4IGVyYXQuIE51bmMgdnVscHV0YXRlIG5lcXVlIHZpdGFlIGp1c3RvIGZhY2lsaXNpcywgbm9uIGNvbmRpbWVudHVtIGFudGUgc2FnaXR0aXMuIE1vcmJpIHZpdmVycmEgc2VtcGVyIGxvcmVtIG5lYyBtb2xlc3RpZS4gTWFlY2VuYXMgdGluY2lkdW50IGVzdCBlZmZpY2l0dXIgbGlndWxhIGV1aXNtb2QsIHNpdCBhbWV0IG9ybmFyZSBlc3QgdnVscHV0YXRlLn0KXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNVxydGxjaCBcbHRyY2hcbG9jaAoKXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjEyXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmOFxoaWNoXGFmOApJbiBub24gbWF1cmlzIGp1c3RvLiBEdWlzIHZlaGljdWxhIG1pIHZlbCBtaSBwcmV0aXVtLCBhIHZpdmVycmEgZXJhdCBlZmZpY2l0dXIuIENyYXMgYWxpcXVhbSBlc3QgYWMgZXJvcyB2YXJpdXMsIGlkIGlhY3VsaXMgZHVpIGF1Y3Rvci4gRHVpcyBwcmV0aXVtIG5lcXVlIGxpZ3VsYSwgZXQgcHVsdmluYXIgbWkgcGxhY2VyYXQgZXQuIE51bGxhIG5lYyBudW5jIHNpdCBhbWV0IG51bmMgcG9zdWVyZSB2ZXN0aWJ1bHVtLiBVdCBpZCBuZXF1ZSBlZ2V0IHRvcnRvciBtYXR0aXMgdHJpc3RpcXVlLiBEb25lYyBhbnRlIGVzdCwgYmxhbmRpdCBzaXQgYW1ldCB0cmlzdGlxdWUgdmVsLCBsYWNpbmlhIHB1bHZpbmFyIGFyY3UuIFBlbGxlbnRlc3F1ZSBzY2VsZXJpc3F1ZSBmZXJtZW50dW0gZXJhdCwgaWQgcG9zdWVyZSBqdXN0byBwdWx2aW5hciB1dC4gQ3JhcyBpZCBlcm9zIHNlZCBlbmltIGFsaXF1YW0gbG9ib3J0aXMuIFNlZCBsb2JvcnRpcyBuaXNsIHV0IGVyb3MgZWZmaWNpdHVyIHRpbmNpZHVudC4gQ3JhcyBqdXN0byBtaSwgcG9ydHRpdG9yIHF1aXMgbWF0dGlzIHZlbCwgdWx0cmljaWVzIHV0IHB1cnVzLiBVdCBmYWNpbGlzaXMgZXQgbGFjdXMgZXUgY3Vyc3VzLn17XHJ0bGNoIFxsdHJjaFxsb2NoCkluIGVsZWlmZW5kIHZlbGl0IHZpdGFlIGxpYmVybyBzb2xsaWNpdHVkaW4gZXVpc21vZC4gfQpccGFyIFxzaHB3cjJcc2hwd3JrM1xzaHBieXBhcmFcc2hwYnlpZ25vcmVcc2hwdG9wMFxzaHBieGNvbHVtblxzaHBieGlnbm9yZVxzaHBsZWZ0MjgxOVxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1XHJ0bGNoIFxsdHJjaFxsb2NoCntcKlxmbHltYWluY250NVxmbHlhbmNob3IwXGZseWNudG50fXtcc2hwe1wqXHNocGluc3Rcc2hwd3IyXHNocHdyazNcc2hwYnlwYXJhXHNocGJ5aWdub3JlXHNocHRvcDBcc2hwYm90dG9tMjY2MFxzaHBieGNvbHVtblxzaHBieGlnbm9yZVxzaHBsZWZ0MjgxOVxzaHByaWdodDY4MTl7XHNwe1xzbiBzaGFwZVR5cGV9e1xzdiA3NX19e1xzcHtcc24gd3pEZXNjcmlwdGlvbn17XHN2IH19e1xzcHtcc24gd3pOYW1lfXtcc3YgfX17XHNwe1xzbiBwaWJ9e1xzdiB7XHBpY3RccGljc2NhbGV4MTAwXHBpY3NjYWxleTEwMFxwaWNjcm9wbDBccGljY3JvcHIwXHBpY2Nyb3B0MFxwaWNjcm9wYjBccGljdzIwMFxwaWNoMTMzXHBpY3dnb2FsNDAwMFxwaWNoZ29hbDI2NjBcanBlZ2JsaXAKZmZkOGZmZTAwMDEwNGE0NjQ5NDYwMDAxMDEwMTAwNDgwMDQ4MDAwMGZmZTIwYzU4NDk0MzQzNWY1MDUyNGY0NjQ5NGM0NTAwMDEwMTAwMDAwYzQ4NGM2OTZlNmYwMjEwMDAwMDZkNmU3NDcyNTI0NzQyMjA1ODU5NWEyMDA3Y2UKMDAwMjAwMDkwMDA2MDAzMTAwMDA2MTYzNzM3MDRkNTM0NjU0MDAwMDAwMDA0OTQ1NDMyMDczNTI0NzQyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGY2ZDYwMDAxMDAwMDAwMDBkMzJkNDg1MDIwMjAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDExNjM3MDcyNzQwMDAwMDE1MDAwMDAwMDMzNjQ2NTczNjMwMDAwMDE4NDAwMDAKMDA2Yzc3NzQ3MDc0MDAwMDAxZjAwMDAwMDAxNDYyNmI3MDc0MDAwMDAyMDQwMDAwMDAxNDcyNTg1OTVhMDAwMDAyMTgwMDAwMDAxNDY3NTg1OTVhMDAwMDAyMmMwMDAwMDAxNDYyNTg1OTVhMDAwMDAyNDAwMDAwMDAxNDY0NmQKNmU2NDAwMDAwMjU0MDAwMDAwNzA2NDZkNjQ2NDAwMDAwMmM0MDAwMDAwODg3Njc1NjU2NDAwMDAwMzRjMDAwMDAwODY3NjY5NjU3NzAwMDAwM2Q0MDAwMDAwMjQ2Yzc1NmQ2OTAwMDAwM2Y4MDAwMDAwMTQ2ZDY1NjE3MzAwMDAKMDQwYzAwMDAwMDI0NzQ2NTYzNjgwMDAwMDQzMDAwMDAwMDBjNzI1NDUyNDMwMDAwMDQzYzAwMDAwODBjNjc1NDUyNDMwMDAwMDQzYzAwMDAwODBjNjI1NDUyNDMwMDAwMDQzYzAwMDAwODBjNzQ2NTc4NzQwMDAwMDAwMDQzNmYKNzA3OTcyNjk2NzY4NzQyMDI4NjMyOTIwMzEzOTM5MzgyMDQ4NjU3NzZjNjU3NDc0MmQ1MDYxNjM2YjYxNzI2NDIwNDM2ZjZkNzA2MTZlNzkwMDAwNjQ2NTczNjMwMDAwMDAwMDAwMDAwMDEyNzM1MjQ3NDIyMDQ5NDU0MzM2MzEKMzkzNjM2MmQzMjJlMzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTI3MzUyNDc0MjIwNDk0NTQzMzYzMTM5MzYzNjJkMzIyZTMxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA1ODU5NWEyMDAwMDAwMDAwMDAwMGYzNTEwMDAxMDAwMDAwMDExNmNjNTg1OTVhMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDU4NTkKNWEyMDAwMDAwMDAwMDAwMDZmYTIwMDAwMzhmNTAwMDAwMzkwNTg1OTVhMjAwMDAwMDAwMDAwMDA2Mjk5MDAwMGI3ODUwMDAwMThkYTU4NTk1YTIwMDAwMDAwMDAwMDAwMjRhMDAwMDAwZjg0MDAwMGI2Y2Y2NDY1NzM2MzAwMDAKMDAwMDAwMDAwMDE2NDk0NTQzMjA2ODc0NzQ3MDNhMmYyZjc3Nzc3NzJlNjk2NTYzMmU2MzY4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE2NDk0NTQzMjA2ODc0NzQ3MDNhMmYyZjc3Nzc3NzJlNjk2NTYzMmU2MzY4MDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjQ2NTczNjMwMDAwMDAwMDAwMDAwMDJlNDk0NTQzMjAzNjMxMzkzNjM2MmQKMzIyZTMxMjA0NDY1NjY2MTc1NmM3NDIwNTI0NzQyMjA2MzZmNmM2Zjc1NzIyMDczNzA2MTYzNjUyMDJkMjA3MzUyNDc0MjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyZTQ5NDU0MzIwMzYzMTM5MzYzNjJkMzIyZTMxMjA0NDY1NjYKNjE3NTZjNzQyMDUyNDc0MjIwNjM2ZjZjNmY3NTcyMjA3MzcwNjE2MzY1MjAyZDIwNzM1MjQ3NDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDY0NjU3MzYzMDAwMDAwMDAwMDAwMDAyYzUyNjUKNjY2NTcyNjU2ZTYzNjUyMDU2Njk2NTc3Njk2ZTY3MjA0MzZmNmU2NDY5NzQ2OTZmNmUyMDY5NmUyMDQ5NDU0MzM2MzEzOTM2MzYyZDMyMmUzMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyYzUyNjU2NjY1NzI2NTZlNjM2NTIwNTYKNjk2NTc3Njk2ZTY3MjA0MzZmNmU2NDY5NzQ2OTZmNmUyMDY5NmUyMDQ5NDU0MzM2MzEzOTM2MzYyZDMyMmUzMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA3NjY5NjU3NzAwMDAKMDAwMDAwMTNhNGZlMDAxNDVmMmUwMDEwY2YxNDAwMDNlZGNjMDAwNDEzMGIwMDAzNWM5ZTAwMDAwMDAxNTg1OTVhMjAwMDAwMDAwMDAwNGMwOTU2MDA1MDAwMDAwMDU3MWZlNzZkNjU2MTczMDAwMDAwMDAwMDAwMDAwMTAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjhmMDAwMDAwMDI3MzY5NjcyMDAwMDAwMDAwNDM1MjU0MjA2Mzc1NzI3NjAwMDAwMDAwMDAwMDA0MDAwMDAwMDAwNTAwMGEwMDBmMDAxNDAwMTkwMDFlMDAyMzAwMjgKMDAyZDAwMzIwMDM3MDAzYjAwNDAwMDQ1MDA0YTAwNGYwMDU0MDA1OTAwNWUwMDYzMDA2ODAwNmQwMDcyMDA3NzAwN2MwMDgxMDA4NjAwOGIwMDkwMDA5NTAwOWEwMDlmMDBhNDAwYTkwMGFlMDBiMjAwYjcwMGJjMDBjMTAwYzYKMDBjYjAwZDAwMGQ1MDBkYjAwZTAwMGU1MDBlYjAwZjAwMGY2MDBmYjAxMDEwMTA3MDEwZDAxMTMwMTE5MDExZjAxMjUwMTJiMDEzMjAxMzgwMTNlMDE0NTAxNGMwMTUyMDE1OTAxNjAwMTY3MDE2ZTAxNzUwMTdjMDE4MzAxOGIKMDE5MjAxOWEwMWExMDFhOTAxYjEwMWI5MDFjMTAxYzkwMWQxMDFkOTAxZTEwMWU5MDFmMjAxZmEwMjAzMDIwYzAyMTQwMjFkMDIyNjAyMmYwMjM4MDI0MTAyNGIwMjU0MDI1ZDAyNjcwMjcxMDI3YTAyODQwMjhlMDI5ODAyYTIKMDJhYzAyYjYwMmMxMDJjYjAyZDUwMmUwMDJlYjAyZjUwMzAwMDMwYjAzMTYwMzIxMDMyZDAzMzgwMzQzMDM0ZjAzNWEwMzY2MDM3MjAzN2UwMzhhMDM5NjAzYTIwM2FlMDNiYTAzYzcwM2QzMDNlMDAzZWMwM2Y5MDQwNjA0MTMKMDQyMDA0MmQwNDNiMDQ0ODA0NTUwNDYzMDQ3MTA0N2UwNDhjMDQ5YTA0YTgwNGI2MDRjNDA0ZDMwNGUxMDRmMDA0ZmUwNTBkMDUxYzA1MmIwNTNhMDU0OTA1NTgwNTY3MDU3NzA1ODYwNTk2MDVhNjA1YjUwNWM1MDVkNTA1ZTUKMDVmNjA2MDYwNjE2MDYyNzA2MzcwNjQ4MDY1OTA2NmEwNjdiMDY4YzA2OWQwNmFmMDZjMDA2ZDEwNmUzMDZmNTA3MDcwNzE5MDcyYjA3M2QwNzRmMDc2MTA3NzQwNzg2MDc5OTA3YWMwN2JmMDdkMjA3ZTUwN2Y4MDgwYjA4MWYKMDgzMjA4NDYwODVhMDg2ZTA4ODIwODk2MDhhYTA4YmUwOGQyMDhlNzA4ZmIwOTEwMDkyNTA5M2EwOTRmMDk2NDA5NzkwOThmMDlhNDA5YmEwOWNmMDllNTA5ZmIwYTExMGEyNzBhM2QwYTU0MGE2YTBhODEwYTk4MGFhZTBhYzUKMGFkYzBhZjMwYjBiMGIyMjBiMzkwYjUxMGI2OTBiODAwYjk4MGJiMDBiYzgwYmUxMGJmOTBjMTIwYzJhMGM0MzBjNWMwYzc1MGM4ZTBjYTcwY2MwMGNkOTBjZjMwZDBkMGQyNjBkNDAwZDVhMGQ3NDBkOGUwZGE5MGRjMzBkZGUKMGRmODBlMTMwZTJlMGU0OTBlNjQwZTdmMGU5YjBlYjYwZWQyMGVlZTBmMDkwZjI1MGY0MTBmNWUwZjdhMGY5NjBmYjMwZmNmMGZlYzEwMDkxMDI2MTA0MzEwNjExMDdlMTA5YjEwYjkxMGQ3MTBmNTExMTMxMTMxMTE0ZjExNmQKMTE4YzExYWExMWM5MTFlODEyMDcxMjI2MTI0NTEyNjQxMjg0MTJhMzEyYzMxMmUzMTMwMzEzMjMxMzQzMTM2MzEzODMxM2E0MTNjNTEzZTUxNDA2MTQyNzE0NDkxNDZhMTQ4YjE0YWQxNGNlMTRmMDE1MTIxNTM0MTU1NjE1NzgKMTU5YjE1YmQxNWUwMTYwMzE2MjYxNjQ5MTY2YzE2OGYxNmIyMTZkNjE2ZmExNzFkMTc0MTE3NjUxNzg5MTdhZTE3ZDIxN2Y3MTgxYjE4NDAxODY1MTg4YTE4YWYxOGQ1MThmYTE5MjAxOTQ1MTk2YjE5OTExOWI3MTlkZDFhMDQKMWEyYTFhNTExYTc3MWE5ZTFhYzUxYWVjMWIxNDFiM2IxYjYzMWI4YTFiYjIxYmRhMWMwMjFjMmExYzUyMWM3YjFjYTMxY2NjMWNmNTFkMWUxZDQ3MWQ3MDFkOTkxZGMzMWRlYzFlMTYxZTQwMWU2YTFlOTQxZWJlMWVlOTFmMTMKMWYzZTFmNjkxZjk0MWZiZjFmZWEyMDE1MjA0MTIwNmMyMDk4MjBjNDIwZjAyMTFjMjE0ODIxNzUyMWExMjFjZTIxZmIyMjI3MjI1NTIyODIyMmFmMjJkZDIzMGEyMzM4MjM2NjIzOTQyM2MyMjNmMDI0MWYyNDRkMjQ3YzI0YWIKMjRkYTI1MDkyNTM4MjU2ODI1OTcyNWM3MjVmNzI2MjcyNjU3MjY4NzI2YjcyNmU4MjcxODI3NDkyNzdhMjdhYjI3ZGMyODBkMjgzZjI4NzEyOGEyMjhkNDI5MDYyOTM4Mjk2YjI5OWQyOWQwMmEwMjJhMzUyYTY4MmE5YjJhY2YKMmIwMjJiMzYyYjY5MmI5ZDJiZDEyYzA1MmMzOTJjNmUyY2EyMmNkNzJkMGMyZDQxMmQ3NjJkYWIyZGUxMmUxNjJlNGMyZTgyMmViNzJlZWUyZjI0MmY1YTJmOTEyZmM3MmZmZTMwMzUzMDZjMzBhNDMwZGIzMTEyMzE0YTMxODIKMzFiYTMxZjIzMjJhMzI2MzMyOWIzMmQ0MzMwZDMzNDYzMzdmMzNiODMzZjEzNDJiMzQ2NTM0OWUzNGQ4MzUxMzM1NGQzNTg3MzVjMjM1ZmQzNjM3MzY3MjM2YWUzNmU5MzcyNDM3NjAzNzljMzdkNzM4MTQzODUwMzg4YzM4YzgKMzkwNTM5NDIzOTdmMzliYzM5ZjkzYTM2M2E3NDNhYjIzYWVmM2IyZDNiNmIzYmFhM2JlODNjMjczYzY1M2NhNDNjZTMzZDIyM2Q2MTNkYTEzZGUwM2UyMDNlNjAzZWEwM2VlMDNmMjEzZjYxM2ZhMjNmZTI0MDIzNDA2NDQwYTYKNDBlNzQxMjk0MTZhNDFhYzQxZWU0MjMwNDI3MjQyYjU0MmY3NDMzYTQzN2Q0M2MwNDQwMzQ0NDc0NDhhNDRjZTQ1MTI0NTU1NDU5YTQ1ZGU0NjIyNDY2NzQ2YWI0NmYwNDczNTQ3N2I0N2MwNDgwNTQ4NGI0ODkxNDhkNzQ5MWQKNDk2MzQ5YTk0OWYwNGEzNzRhN2Q0YWM0NGIwYzRiNTM0YjlhNGJlMjRjMmE0YzcyNGNiYTRkMDI0ZDRhNGQ5MzRkZGM0ZTI1NGU2ZTRlYjc0ZjAwNGY0OTRmOTM0ZmRkNTAyNzUwNzE1MGJiNTEwNjUxNTA1MTliNTFlNjUyMzEKNTI3YzUyYzc1MzEzNTM1ZjUzYWE1M2Y2NTQ0MjU0OGY1NGRiNTUyODU1NzU1NWMyNTYwZjU2NWM1NmE5NTZmNzU3NDQ1NzkyNTdlMDU4MmY1ODdkNThjYjU5MWE1OTY5NTliODVhMDc1YTU2NWFhNjVhZjU1YjQ1NWI5NTViZTUKNWMzNTVjODY1Y2Q2NWQyNzVkNzg1ZGM5NWUxYTVlNmM1ZWJkNWYwZjVmNjE1ZmIzNjAwNTYwNTc2MGFhNjBmYzYxNGY2MWEyNjFmNTYyNDk2MjljNjJmMDYzNDM2Mzk3NjNlYjY0NDA2NDk0NjRlOTY1M2Q2NTkyNjVlNzY2M2QKNjY5MjY2ZTg2NzNkNjc5MzY3ZTk2ODNmNjg5NjY4ZWM2OTQzNjk5YTY5ZjE2YTQ4NmE5ZjZhZjc2YjRmNmJhNzZiZmY2YzU3NmNhZjZkMDg2ZDYwNmRiOTZlMTI2ZTZiNmVjNDZmMWU2Zjc4NmZkMTcwMmI3MDg2NzBlMDcxM2EKNzE5NTcxZjA3MjRiNzJhNjczMDE3MzVkNzNiODc0MTQ3NDcwNzRjYzc1Mjg3NTg1NzVlMTc2M2U3NjliNzZmODc3NTY3N2IzNzgxMTc4NmU3OGNjNzkyYTc5ODk3OWU3N2E0NjdhYTU3YjA0N2I2MzdiYzI3YzIxN2M4MTdjZTEKN2Q0MTdkYTE3ZTAxN2U2MjdlYzI3ZjIzN2Y4NDdmZTU4MDQ3ODBhODgxMGE4MTZiODFjZDgyMzA4MjkyODJmNDgzNTc4M2JhODQxZDg0ODA4NGUzODU0Nzg1YWI4NjBlODY3Mjg2ZDc4NzNiODc5Zjg4MDQ4ODY5ODhjZTg5MzMKODk5OTg5ZmU4YTY0OGFjYThiMzA4Yjk2OGJmYzhjNjM4Y2NhOGQzMThkOTg4ZGZmOGU2NjhlY2U4ZjM2OGY5ZTkwMDY5MDZlOTBkNjkxM2Y5MWE4OTIxMTkyN2E5MmUzOTM0ZDkzYjY5NDIwOTQ4YTk0ZjQ5NTVmOTVjOTk2MzQKOTY5Zjk3MGE5Nzc1OTdlMDk4NGM5OGI4OTkyNDk5OTA5OWZjOWE2ODlhZDU5YjQyOWJhZjljMWM5Yzg5OWNmNzlkNjQ5ZGQyOWU0MDllYWU5ZjFkOWY4YjlmZmFhMDY5YTBkOGExNDdhMWI2YTIyNmEyOTZhMzA2YTM3NmEzZTYKYTQ1NmE0YzdhNTM4YTVhOWE2MWFhNjhiYTZmZGE3NmVhN2UwYTg1MmE4YzRhOTM3YTlhOWFhMWNhYThmYWIwMmFiNzVhYmU5YWM1Y2FjZDBhZDQ0YWRiOGFlMmRhZWExYWYxNmFmOGJiMDAwYjA3NWIwZWFiMTYwYjFkNmIyNGIKYjJjMmIzMzhiM2FlYjQyNWI0OWNiNTEzYjU4YWI2MDFiNjc5YjZmMGI3NjhiN2UwYjg1OWI4ZDFiOTRhYjljMmJhM2JiYWI1YmIyZWJiYTdiYzIxYmM5YmJkMTViZDhmYmUwYWJlODRiZWZmYmY3YWJmZjVjMDcwYzBlY2MxNjcKYzFlM2MyNWZjMmRiYzM1OGMzZDRjNDUxYzRjZWM1NGJjNWM4YzY0NmM2YzNjNzQxYzdiZmM4M2RjOGJjYzkzYWM5YjljYTM4Y2FiN2NiMzZjYmI2Y2MzNWNjYjVjZDM1Y2RiNWNlMzZjZWI2Y2YzN2NmYjhkMDM5ZDBiYWQxM2MKZDFiZWQyM2ZkMmMxZDM0NGQzYzZkNDQ5ZDRjYmQ1NGVkNWQxZDY1NWQ2ZDhkNzVjZDdlMGQ4NjRkOGU4ZDk2Y2Q5ZjFkYTc2ZGFmYmRiODBkYzA1ZGM4YWRkMTBkZDk2ZGUxY2RlYTJkZjI5ZGZhZmUwMzZlMGJkZTE0NGUxY2MKZTI1M2UyZGJlMzYzZTNlYmU0NzNlNGZjZTU4NGU2MGRlNjk2ZTcxZmU3YTllODMyZThiY2U5NDZlOWQwZWE1YmVhZTVlYjcwZWJmYmVjODZlZDExZWQ5Y2VlMjhlZWI0ZWY0MGVmY2NmMDU4ZjBlNWYxNzJmMWZmZjI4Y2YzMTkKZjNhN2Y0MzRmNGMyZjU1MGY1ZGVmNjZkZjZmYmY3OGFmODE5ZjhhOGY5MzhmOWM3ZmE1N2ZhZTdmYjc3ZmMwN2ZjOThmZDI5ZmRiYWZlNGJmZWRjZmY2ZGZmZmZmZmRiMDA0MzAwMDUwNDA0MDQwNDAzMDUwNDA0MDQwNjA1MDUKMDYwODBkMDgwODA3MDcwODEwMGIwYzA5MGQxMzEwMTQxMzEyMTAxMjEyMTQxNzFkMTkxNDE2MWMxNjEyMTIxYTIzMWExYzFlMWYyMTIxMjExNDE5MjQyNzI0MjAyNjFkMjAyMTIwZmZkYjAwNDMwMTA1MDYwNjA4MDcwODBmMDgKMDgwZjIwMTUxMjE1MjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMGZmYzIwMDExMDgwMDg1MDAKYzgwMzAxMTEwMDAyMTEwMTAzMTEwMWZmYzQwMDFjMDAwMDAxMDUwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAwMTAzMDQwNTA2MDcwOGZmYzQwMDFhMDEwMDAzMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDIKMDMwMDA0MDUwNmZmZGEwMDBjMDMwMTAwMDIxMDAzMTAwMDAwMDFjZWY5MGZiMDlhNzI5NTJkNmE3NWNiZWE0ZDRlNjNjOGZiNWU0NmI0N2I3YWMxM2VlZmJiYzYwMGQxYWIwMDc2MDU2Y2UwMTAwNDE2YWNhZmNmNzJmNzY0YzUKYjJjODhjYjc3NzVlM2RkYTcyNjRmNTc1NGVkMzM2MGRiMDM2MDFhYTExMDJkNmM4MWJjZmNhMzk4NDExYzU2Y2UzMmQ5ZDQxMDFjODQ3YmI4MzRlOGJiY2VhODg5MTZiZWFkZDNlM2RiNjkwNzUzNGI0OTg5Mzg0YmQzYzZiNTcKYjFkY2ZkMDk4NDZhZjFhYjM2NjAwNDRlNTgyZDk2Y2UwYjhjOTc3OTgyZjZmM2QzN2I1MjU3MDM2YjlmYWJkNGZkMGYwYTQ1NThmYTVlNWFjZjNhNWQzZTJkNGVlY2JiYzM2MDRiZGJiOWE0ZGIwZTYxMTgxODMxNTdjMTYwZjgKYWM1ZDVhMzQ3ZjE1YTc0NDEwNzI1ZDYyNjNhZmU2ZWFmNDhlZWYwOWQ3MDc0OTk2YWEwYTdjZWEzZWI3MDVkNTJjNGU5ZTRmNjhlMGFmNTRhODNiMzMyODk0NzJhZTU1OGU0MzJjNWMzNTE0YWY4Nzc2MzQzY2VkNjI0ZDdmOWYKYTNkMWEyN2IyYjc5ODYxNGZiOTQ5ODM5MThmY2RkYmUxZmQ3NGMwZTllNGQ3NGRlZWRlNzVlNDY0NzY0NDUxMzIyMjExMGI2NDBhMGQ4NDJiYzVkZWI5NDBlMDE0OWY5N2I3ZGFiOGQzNzRmMTk4NWIzZTk0NWQ4MjFhMDk1N2MKYTM3NmY5NWZhM2MzNTllNWVkMWU3ZjU3NzMwMDZlOGU1MTMyYjE1NjIxNmM4MTYwNzkxN2I2MGQyYTI5NGU3MTg1YjhkYmQ1M2NkYmM5MjYyNTVlOGJkYWYxZDZjMmE2MzQ3Yzg5ZjU3Y2YzZWE3MmUzZGI5YjY5MGZiZjc5OWQKMzY4Y2RkOTVjYTI2NTEyMTYwOGUwNTZmMzRlOGI1MTZhNjI4Mzc2NzVlYTc5NmZhNWU2N2E0YjlmYTZjYTZmNDFmYTdmOTFlN2JjY2Y1ZjNkOWYwM2JhMTZkMjdjMmQ1MzEyZDBiMTk3YjI5MWQ2NDUyNjRlOWQ2NTJiYTA2NWEKNmFmNjZiMmE4OTRmMzBlOGU5YzEyZGFlOTRiODk0ZThiOTJkNjdjZWY0YWRjYWI2MjY3YjNmNjdlNzIwOGQyYmFkNzM5NmY4NGRhYmJjYjYxMjc3NDI0ZjQ5ZDVhMmQ4NjlhNzQwNzQ4OTkyMDBkMTgzMTExOTI2OTFlYTQ3YTkKNTQ1M2ExZTZiNzVjYTk3ZWRjZDYxZTNjY2Y5M2VlMzZjOGUwMGQwOWQxYjRlODVhMTE3NTcxZWFhMjQ4Yzg2NTZhYjJlNTU0NjdkMDQ0ZGEzM2EzMjY5MTZiZDM2ZDI5M2NmM2FlZjIxZTkwMDE0NzE1MzhmYzNlODU3ZDUwNjEKMDE1Mzc5ZDgyOTQzNmMyZWZmMDAzZTc2OWM4NDM5MDQ0MmRhMjZkMjBkNjA4ZWFkOTJjYmViMjBhOWIyMGM0YWM5MTk5MDhjY2YyMWU3ZmJjZTUyOWQ2NTU3YTc5YmI2YmYwZDIwNDUxZGNhY2U1NGNhOTk1MzIxYzgxZDkzMDgKYjFhMjhkOWFhZGFlZjNkMTc1OWYzMTAyNGFlZTE5ZDc3OWQ3OTllZTk5NGFkNDllN2QyNzRhOTM3MmI2OTk2ZjY0YmU4Yjc1MzU4MDI3MTlmMDEzYTIyNTg2NmQ5ZDczMTExNjMyMzA5ZTgyZDkzMDU0Njg0ZGY4NGUxZjU1Y2EKNDZjODBjODhhOGIyZDhjYjMxNTk3MjMzMjkxNWI4ODZkYTk5MTRiNmNmYjJjNjFjNjNjNTBjYzBiZTMxMTc4MGIzNjZjNzRhY2Q5NWNhOGIyMWUwNTk0NWQ2NDJiMzY1M2MwYjAwNmQxOTEzOGQ3MTBjZThjODEwYzYyMjQ3NjYKMDQ4MzE2MzA5N2FmOWI5OTcxYTczZDJlMDhhOGIyY2I4NGEwMWIyMzkwNzgxNjA2MzJkODViNDRjMDc2YjJiYWU0ZDk5NTgwOThmMTczODMzNTcyZjUxY2ZmMDBmZmM0MDAyNzEwMDAwMjAyMDIwMjAwMDUwNTAxMDEwMDAwMDAKMDAwMDAwMDEwMjAwMDMwNDExMDUxMjEwMTMxNDE1MjEyMDIyMjMzMDMxMDYzMmZmZGEwMDA4MDEwMTAwMDEwNTAyYWYyZDU2OTVjYjVkMjY1MjZjZTVkN2FmNTE1OTdhZjIyYTA5OTk2YWJiZjFhZWExNzhjNzA3MzM3ZmE2ZWIKN2EwYjczZWE0MmY5ZjZiYzJkYjMzOGJhYjU1Mzg4YmM2ZTM0MWM3ZTM0ZjZmYzc5ZWRiOGYzZGFiMTY3YjU2MzQ2ZTEzMGNjZjY1YzU1OThiYzdkNzhkNjdlOWU2MmRlYjQyOWQ5ZGNkY2FiZWU3YzQ0ZWI1YjQxZTBjZWE4MmYKZTVmMTY4MTkzZmU4ZWMyMzg5Y2ZmNWI4N2ZiMzliYmJmM2Q2Njc2OWI5ODJiZGIyMjkxYWE4YzFlMTk5NTBiYjFlZWE0ZDc2Mzg5YzJlNmZhNGNmMGRiMWZhOWNlOTM5MGI3Y2NlNDAxZDBlZDE3ZTRmMTY5YjcxZjAwYzFlMGMKMzYzOThjNmViMWM3YzNlYzFlMTczYzY1ZTE2ZTZmZjRlNTM3NWFhZmE2YzE3NzliMTVhMjRlMjEzZjEwZmE3MzI5MTZkMTk0YTY4YjlkYTcwZjlkZTkzMzkxYzNhZmU5Y2Y3ZDU2Y2ExYTViODhhZDJjYTFlOTk1NjQwMzM4ZjQKZTk0N2QyZGZjZmY0MTg3MThjZGNlMDczZmQ0NjJmZTljZmIzZWU4Y2MwMGJlZDE2YjUxODRhZDcwYjg1NGJlYTg0ZjU1M2IwOWRkNjc3MTBiNGNkYTQ1ZDQ2NmQyNjhjOTljNjY1OWM0Y2RhNmU0YjJiZWUyNzcxM2I0ZGNkODkKYjEzNzE5YmVjY2RiOGI2NWY5YzEwNWI5MTY1Y2Q4ZjQ3NDk4NDM3N2RjZGY5NTRmODM1NDA4YjAxNDhkN2Y5MGFmY2JkZDY0YTMzYjJhZTRlNGIxZmI1YzM4ZDc2ODM4NmI4Y2MzYzZjZmM3NTZmNzAyNDU1Yzg5NmFlOTZlYTQKNmE3YzE4Mjg3ZjM2YzUzZDJjM2Q3MWYyN2NjNDZkNTk3MzU1NTJhMGRjZTNmZjAwZTA2MzY0MzE1YzZiODRmNGY2YzM2NmU3YzQ2NGFkYTNhNTZiMmZlZDI4MmNiMTIwZWIwZDlkNTE3MjJkODMyMGMzOTY4MGZhY2E4YzM5NTQKNDM5YjU0ZjVmNWNiNmZhZWNhY2Q4YjNhZDZkMGUzNTI2MWMxMTMwNjg2MDg4ZWU4MTZjNDZmMGRjZGMwZTU2MWIwYzJlNjdkZDJmNmIwMmQ2NGVjNzQ5ZjZjZDg4ZjVhYmNiNTNjYmYxZGVlMzIzYjBlYjljYTU1NzMzNGI0ZTQKNzUwOGMxMzhkZmU3YzE5ZDA0ZDM4OWI4NGZjZjcxMGRjMDRhY2I1YTNhNThhMmQ2MDZiMWM5NjBhYzFjYjcxOTNkZTM4YjllZjVjNjRmN2JjMDllZjU4ZjA3Mjk0ODNlZjE2Y2FmOTBlNDZkMzhlYjk0Y2I1NjM1NzRjMmNiMzcKM2UzYzM1MzUzNDYwYjI3Nzk2N2YzMWYxYmNmNjQzNTIyZTU1MmY5MDEzMGVhZTllODcxNzYzMGYxYzRmNGY4ZjNjOGEyNzkzNGNmMjY5OWU1ZDAyNzk5OGM5MWIzNTA0NmNkYzgzM2Q0NjZmOTg5OWQ1YzViZDFlNmM3OGVlNmUKNmNjZGVjMTY5NjIwNjlkNjA0MzExMWEwMGMyMjViNjA4MmY1OGI2MDMzNzM3YTllNjA5ZTY2ZTdlNDlmOTNjM2UyNzk2ODYwMzYyYzE5NTYwODk5NmE2M2Q4ZTVhYWIzZWNkZjgxZjk5YTg2YTA2MDRlYjA2YTA5YTlkNjBkYWMKNWJmNTA1OGFkM2UzYzM3M2I0MjY3NjliOWIzZTBjODFhNzU3NTllNzVjYjAzNmZlOWZlY2Q2YTBmYTA4OWYzMTZjNjExNmY5ZDgxOWE5YTNlM2I4MGNkYzMwOTk1YjE4MGZkMDIwOWE4M2U4ZDQyMjZhNzYyMjJkODYwNmRjMzMKNTM1ZTA2MTMxOWE3ZmZjNDAwMjkxMTAwMDIwMjAxMDMwMzA0MDIwMjAzMDAwMDAwMDAwMDAwMDAwMTAyMTEwMzEwMTIxMzE0MjAyMTA0MzAzMTQxMjIzMjQwNTE0MjUyNzFmZmRhMDAwODAxMDMwMTAxM2YwMWU3NWI0NTkwNTkKNTBiMjQ0ZTU4ZDhlNzEzMzRkMzY3YTU2YThmNTRlZDMyYmQ5YzU4Yjc3YzhiMTFiNjI4YjEyM2Q1Y2FiYzEwNjNmNTczM2FiYzg3NTk5MGViNjY3NTkyM2FjOTdmNDczYTdmNDQ3M2E1ZjQ0YjJhNmJkYWMzZTExNjU5NjYzM2QKNGNhZTQ0NThmNDhjMWNiZTA1ZTk1ZmY5MGEzOGUzZjA4Y2IxNWZiNDQ1ZGQ2NTk3ZDg4YjJjYjNlMjA2NTc3MzEwZjRmNGYzZGIyMjRhODkxMWZmMDA1MWM2YmI2Y2IyY2IyY2IyMjU5NjM2NDdjYjMyYmE4OGM0M2QyMmM4Y2IKN2MyYzY0OGZkZDU5NWQ5N2E1ZTk2NTkxMmQ5YmNkYzYyNDdhOTdmODhmYjcwNGU5ZDEzZjBjOTMzMWNlOWQwYzYzMWY3Yzc0YTJhOGM1MzQ2NzdlNmJiYTI0ZmYwMDI4NThjNjQ2N2JhMjNlZmJkMTMxMzJjYmJmMDQ1MTFjNWMKOTZjZTk0ZTk0ZGFjZDhjZDhjNTEzMTNmYTMyMmE2MzMxY2E5OGZiZGNhODRjNDU5NzY0NTUxMTY2MWYxMDJmNDhlNDY5OThkYTkxMjQ5OWUyYmMxMWIyNmFjZTMzOGY1NmNkY2M3MjE0YWY0Y2RmYTk4ZGZlM2FhMTMxMGIyNDEKN2Q5Y2QwZmVjZTdjN2ZkOGIxZDFmZjAwMDRlNDIxYzkxYmM3MjFiMWY5MWM0NmI0N2E1OGRkODg0YzQyMTExNmJlYzk0MTRiZTA5NjM5MmQyOGExYzE0ODU4ZDBlMjhhNDY0NWUwYjFmYjU2MjYyOTBkOWU0ZGU1YzVmYzk1YWUKZTFlNDhhZjkzN2M1ZmQ5MjVlMjg3MTZiNGEzNjMzNjMzOGQ5YzY3MThlMjk3N2Q5NjVhMjkwYzY0ZTdiNDc2ZmU0OGNmNjlkNDQ4ZWEyNDczNDhlNTkxYzkyMzkyNDZmOTE3MjM2Yzk5YzcyMzhjZDgzOGIyYmIyOGExYmQxOGYKNGEyODZiNGRjNWViNDU2OTQ2ZDI4YTM2MjM4Y2Q4NmM2NmRkMWZiZjVhNTY5NWEyMjhkYTVlOGZkZGIyZmQ5YmZlNGZmZmM0MDAyNzExMDAwMjAyMDEwMzAzMDUwMTAxMDEwMTAwMDAwMDAwMDAwMDAxMDIxMTAzMTIxMzIxMTAKMzAzMTA0MjAyMjQxNTEzMjcxNDA2MWZmZGEwMDA4MDEwMjAxMDEzZjAxOWZhNjZlNjRiZDMzMjVlOWU0MmY0ZjMzNjI0OTEyYzUyM2QyNjM3MTQ3YWM1ZjIzMGFhNWRhOWNhYmMwZGI2M2IxMmFlOTgxNWNhYzkyMzQ5YjY2ZDIKMzY0ZDkzNjRhOTJmYjI1NmZjOWNmNmE0YWQ5MjU0NTE0NGY4NDYwNTUwMjQyMTBlNWE0ZDZkZjgxNjM2ZmNiM2Y4N2E1Zjc2MzEyNjhkMjUxM2U1OTA1NTE0ODkxMWU5OTNmNDhjYWM0Y2NiMWI1NjVmNmE4NjI1NDg2OGQyMzIKYWU1ZDE4ODQ0OTEwZTFkMDg0NjQ4ZTg5NzZlODZiOGExZTJmYzFjNDk5OGQ3Y2FmYTMyODVkMjZhYjkyMWNhYjEyMzJjMzU0N2I3MTVjZjRiMzg3ZTRjOTg1YTIyYmQ5NDMyNDYwOTUzYTE3NGNkMGQyZmI1MDQzMjhhYTI3MmUKMTkyOTY5ZTBkYzFlNDM1YTM3MTFhZDBlNDc4NzY2Mzc2YmE2NDhlYTQ1NTdiZDJiMWFhMjBiODI4YWEyNDY1ZjA2NDU2YzY4Njg5NjA1NDNlMDRhY2M3ODIzNTcyNjY0ODQxNzgzMWM4ZGM0NmVhMjUyODQ4NWEwZjgxYTUwYTAKMzhkNzRjNWZkMTNmZTg1NWQxOTQ2NWYyMzRjNzA2Mzg0ODc5NWIzZmQzODM1MTFmZjQ5MmZmMDBkMjhhMTcwNTk2NzIyYjM5MzQxYjZjZDJmZWNkMjcyNmE2NmIzMjczZTA4YjY4NTI0NzA1OTY1OTdkNzE3OTJiZDg5OGJkOTYKN2MwNzVmYTNhMThkNTNlOTVlY2EzNDlhMTlhMjQ0Nzg2MjhkOWI0Y2RiMzQyMzRjNGY4MWFhM2ZhNWMwNTRmYzE1ZDFmNWExYTM0OGUyZmEyMTBhMzYyMWFkNDZjYzRkOThmZTliMzFmZDM2NjFmYTZkNDNmNGQ5ODFiNThjZGIKYzY4YmM2OGRlODhmMzliYTlmZDhhNjZiMmQ3NjEzMmNkNDZhMmM2OTFhMGFhZWI0NTFjMGViYWVhNjg1OTE5Yjg2YjE2NDhmZDhlNGJlYmRhOTllN2FkOTY3OTM0OTVkYWIzNTE2NTdiYWZkYjdkMjkwZTNkYWExYWZmODFjN2IKMTQ3ZmZmYzQwMDM3MTAwMDAxMDIwMzA0MDgwMzA1MDkwMTAwMDAwMDAwMDAwMDAxMDAwMjAzMTEyMTEwMjIzMTMyMDQxMjEzMjAzMDQxNTE3MTA1NjE4MTIzMzM5MTkyYTEzNDQwNDQ1MDYyNzI4MmIxZDFlMWZmZGEwMDA4MDEKMDEwMDA2M2YwMmNhNjY1NjUyYjAzNjYyOGRlNDZhYjE1MTI0Nzg1NDU1NzZiMTU3NGVhOGI1YmYzNTk5MTY0NTkxNjQ1OTAyYzgxN2JiMGFlYjY0OGI5ODJhNzg1MjA3MTNiYmY0ZGM5Yjk2Nzk5ZjI0NzYwYzk3NzQxZTczNzMKZTJiNjFmNGFlZTMwMjFlNzVkYzczN2E4NGU2YmFjZDQ3MWY2NzEzODg1NDRmMmE2ZTM5ZGQwMjk2ZWVkMDBiMjYzMTA5YjMzN2RiNDNjMzI5ZDEwNzMzMzU3YTk2ODc3NTMzZGUyZDRlODY2YzEzMzcxZjQyODExYzIyMmQ5OGMKMTAwNTM0NzQ2ZWZlZDk4MmQwYzcxYmNkYTcwODM2Y2FhZDkzNTQyNmNiOWE5NzVhZWU2MzZiOWE1Mzk5NjM1ZDNiYTY4NTA3MDNjMDlhNzAxYzk1NGFkNTYyOTljNTZiNzI2ODlhMjNhNTM3MDhhYTJmNzJmNjUwOGE5NDc2NmEKMjliNDRkNjUyYThkMjgwYWI4MmE0MzQzMDAxNWVjNmRkNmQ2YTVhZTc2Y2NkNTU2ODJkNzNiY2Q0ZjY2NTY0MmIyMTU4YWFhYWI2NmE5MGZlOGE5MGRjN2IyOTFkMGRjYWIwNjRiMDU0MGFhYzBiMmE5MmFhY2NhODZjOWI1ZjUKMWM5NTU2NDA1NjEyNTc2MjdjNTZhOGE5NmE5M2RiM2RjYzU2MzY2MmIzMjEyMjdkMTQ4Y2ZlM2I5NTU1NjRjNzUwYmE4NWZlZDBhZWFhOGQzZWFhZTQzNzkxZDk0ZTI2ODJmMWU2ZGEyZDY5MTFlNGYwYjVjY2JkMTNhMmM4YzgKZDJkYTFiM2M5NTU3NTU3MTg0YTk5NjQ4MjI0MTUyN2M2ZDRlZWQyYmVkN2YwNjE1ZWZlMjFlZDA4YWZjNDNiYjQzNTc3NDVkMzFkZmMxNTNjMzc0YjNlOGE3MGZjMGUyY2ZkMDJiYmUwNmZmMDA5ZDRhMTc4MWZhYjljYTdhNGIKMjE0M2ZkMmNhYWJiYWU3ZjdiY2JiZmI1NTkyYWYwMGE5YzU4YTE4Y2VmNTI4MzU4NWEwMjAxOWE0ZWNjMDQzNjkxMjZlZWExNzIyYjkyZTRiOTViOGFmZjAwOGFlYzM3Mzk1YzgyMDc3NTM4OGRkNzZmNDA2NGFmMzVkMGZiODUKNDcwM2MxOTg1MjU5OGFjNGFhMmE5OWFhZDE1MzcyZWFlNGFiNmNjMmJiMTBmYWFiYzI2YWI0NTM2Yjk1ZWM3ODc0NTdhZGMzODM3NWRmNzBhZmU0OWZmZmM0MDAyODEwMDAwMzAwMDEwMzAzMDQwMjAzMDEwMTAwMDAwMDAwMDAKMDAwMTExMjEzMTQxNjExMDUxNzEyMDgxOTFhMWIxYzFkMWUxZjAzMGYxZmZkYTAwMDgwMTAxMDAwMTNmMjEzNTBiNjdmMTE5NDZhZjYyOWU1ODljYjBkMDgxZjRhY2M5MTQ4NDBjZDAzNjM2M2Y1MzU0OWEzN2I5ZWQwNzQyZDkKZTBjNzU5YmFjNmQzYzFhMTc1YWRmOGU4YTJiNDdjMWQ5MGUzMGRiYjNlMGZmYzAxY2MzNWJmODhmZDA0Mzc2NmU5YjBkOGRmYWY0ZTBlMDZmODI4NWQyYjNmMjIzMDJkMTIyYmQyOGJiMjJlNDc5YThhZGIyMTE5YzM2NTZlY2EKNzBiZDk4ZDhmYTMxYjJmYTliOGE5N2FiNDUxOGFmNGU5M2NhMTU5Y2NmZDA2ZjE4NTcyYzMyMzQ3MmU1YzJmODYyMTZkMzI5NDZjNmZkNzc0NWEyNzUzMDlhMmE2MzA3ODEwNWVlNjIyNmQ4ZTg0ZWExZjZiMDg2ZTY2NThjYTkKZmEwMDg0YmQ2ZmE2YWY4MTk4MzZkY2MyOWMyZDA5Nzk0ZThmODMyMjVmYWNiNGZiMThhMjQyZThmNTJiODMwNDMxZTBjYzMxNzgzMDFhNTVhOGJmZjFlNTZjMTVhYTE2YmMxOWE3MDM0YjBjYjJhY2FhZjk2MjY1MjEzYTJkMGMKNGI2OWFmNDliMjc1NmE4ZWVlNzg0YzVlOWE1Mjk2YTM5MjhjMmRhMGIyOWI3NTNiMjQ2OGI5NzljOWY4ZmQwYmJjNGY3MjVhZjQ5NWQxODhiMDRkNTZhMWNhNThiODFiMzVmMDQxMjY1YTY4ZTUzOTQ4ZWU0MWNjNWI3MWEyNTUKODg0YzE4MDYzMDFkMTJkODdmNzE3YmFlZTMyMzU5NDFiOTlmZDA1ODRmMDMyYWEzYTQ1MGQ2MTc4ZDg0NzcxNWViMDgxMzY4NWI5OGJmNzNmOGNjNmExOTkxNGQ5YjE5NzUzNWRkOTRlZWU0NTE2YTg0ZWI0NGIwYWI3YjEyZWMKNzkzNzljOGFmMmRhYjUyMzc2ZjY4ZDIwNzExMjQzNWJkMWRlNjRhNDU1ZTRkNjA5MDliMjBmODE4YTMxMGQwMWY2MDhmN2MyMjE3YmM5MzQ4NmJmOTY3MDUwODc0MWIxNmJmMTg2ZjU5MjkxODY1ZDE5MTk1MDI4YjJiMWU5YmEKYzg2MWIwNzQwYjNjYTFiZjk0YmI5YTdiNzhiMWNkYWUxMDVhYzQxYjZkNzc2MjVjMTViYTM0YTg2MzY4NjhhM2E0MTE0NTVlNDY3N2U4NGRiZTQ1ODAyYTFhY2Q3MjMxN2Y2MjdkYThlMTQ2OGVjZjcxZTk3ZWFlZjIzY2FiMTYKYTI2OWU0YjVjYTljZGIwMDdhMDk3ODM1OWY5MzUxMzViMjdlYzVlMTY5OTIxZTQwMzcwYTQ5NzIxN2JhMjkwNjVjYTk0ZDZiNTBlNzRkZWM3NDE0Y2QwNjJmZDQzNWVhOWU0ZDI5YTBjY2Q0MmQ3MDM2YWYyMzU2Y2NiZTI1YzAKZTMyY2I2ZmQwNDIzYWZmOGY4MTI0YzI3YTdiYzA5OWY0MjZiZjczMjZhMmE1YjkwYWJhZGJlZWQzZjgyNmMyZTAyNWYyNjUwN2VlMzdjOTZkYjM1ZWZmOTAwYWM0ZDViMWMxZDA5MmQ4ODdhYTM4YjE4NTM4N2JhMWE3NzFlZTYKYTlmZDE4NTkxODE1NThlODkzMTcxYjNhMjU2YjEyNTc3MjgyNGNhZDM2Y2U2MWExYWY4YTMzZGQ1ZWM3Zjk0MTJmYjdkMWUxZjkyYWFkN2U0ZTI3YjUxODVmMTRhMWY3ZDIxZTRjMWU5NTI3MWZlYjIyZWY4ZjYyNzk1NmNjNTcKYjkyOWNhNDA5Y2IwZjI4YmQ0YzhmNTg3ZDA0NzE5MTE0NTNmMjI5MzM0ZTQ4MGQ4YzZjMWM5YzgwZDQ3NDRlMjg2MDc3OGQwZjQxM2U0Njk5NjgxMjRkYTU3YjhkYjZhMzYyMTFmNzU4MzYzMWRiMjMxNjlmMDNiODFjOGM5OGEKMzI2ODUyMmI3OTRmMjc3ODRiYjE5MTI4OWIxZGE0NDE5MGM1ZjVjZDEyMWE3MzIzNzY2NDQ4MjkyMGNmYTBkMmU4MzQ3YTFjODI3ZGNlMDBkYzIzZDZlNDkwNGM3OTM0MjkyMDRmYTNhMTUxMzdiOTI4ZDIyM2MxY2I4ODc4YzMKYTM3NWEzMWE4N2NhMTQxZjUwN2ViMmI3NDZiYTMwOTQ4NWQ0ODU5MjA0MGMyZDBiZTlkODg0MzFhOGMyMTg3OGU0OGZmZmRhMDAwYzAzMDEwMDAyMDAwMzAwMDAwMDEwNmIwNjMxYThmNmU0NGUwYzc2Y2ZjNmExOTdjMzRiOWMKZDVhNWUwZmI1NDAzODQxMmRhYmY0ZDIwMGRhOGViY2Y2NmVhMjY0MjA3MGUyZWNiOWU2ZWYzZGFhZjQ2YzY0MTg0YjcwNDkwMGUzMmZkNGVkMTMzY2Q1ZDZkODBiODEyNDEyY2JlODA5YWUxOThlNzcxMDA5NDkyMzNmZjAwMmUKMjBkZGIyMzM5OGM5YjA5MTdjYjVkYTRmZTE4MmJkNmI5NGYxZDVhYjEwMDM1MTI4N2M3OTAyZTViNjgxMzkxNDRkZDhkYzNlM2RmYmM3ZTgxOTlkMDA5OGNkODE5ZmNkOWNkZDQ4YWIyMjQ5MjgwZTc4MmZjMThmZjk1MjdhOTYKMjlhNDI5MjIxMGY0NjVjYzUyNzE1YWJhMGVhMzljNmViZmZmYzQwMDI3MTEwMTAxMDEwMDAyMDIwMjAxMDMwNDAzMDAwMDAwMDAwMDAxMDAxMTIxMzExMDQxNTE2MTIwNzE5MWYwMzA4MWIxZDFjMWUxZjFmZmRhMDAwODAxMDMKMDEwMTNmMTA0Mzg5Y2MyMWM5NjZiYjhiNmRkYWNkYmIzMGU2ZTE3NjAzMTc2ODIwODNmMjI2YmQyMjBjMzgyM2FhNGVhZDM4YjAzZmEwZmYwMDlmMDgzYTA5ZmFiYzFmYTM3ZDY1YTcwOGJkYzM3NDE4OTg2N2JiMzk4MjBmY2IKMjFjZmUyYzY3MmU1OTAwZmQ1ZjEzOTY2ZjIxMzk3OTBiZDhmZGNlMDJlMTg0NDQxZTM3ZjAwM2UwMzU5ZTAxNzAyZTRjZTljOGU2NmQwZmFlM2M1Y2Q5M2JlZTU0OGMyNjZhZmE3ZmNkYTMxODIwYmE5NjdmMDAyMTE2YjFjYWUKNWM0MDFlMjgxOTBkNzVmMDUzNjBkZjY4MmNlNWJkZDI1MDRmNzFlMGIyY2NkZjBkODg0N2NjMDNhNDcyZTY3NTdiOWIxYzFmYTdlZmZmMDA1MGIyNTk2ZGI5OTc0Y2Q2MmU0YjliNzRkYzY1Mjk0YjJkYjZjMzBjYTFiMmNiZTQKNGMwYjcxMTJjOTZkODllMzcwOGY2NGU3OTY1M2VlNTJjYjJjYjZkYjEwN2NjMTFkYjE2Njk5MmFmZWNmZjNmNzk1NmExYmFiZWJiMWVjOTMzNmY2YWRmMjU3MWNmNGNhNjY1OTk2ZDgwNmI2YzY5M2UyMzE2ZjgxZTI3YjJlMjcKZGYzZmJjNDYxMGMyOGM0NjI1YzM4MmY2MjY3OGNmY2U0YzA4NjMyZDkxYzc4MTRlZWQ1MTZiYTViMDg3ODhkNjc5ZTBlNjY3Y2M0NjQwN2QyYzNkNmVjMjQ2OTA1YzZjYjRkNTg0NzUyMWY1NjkwYzA1MDdjYzA3YjljODE2NjMKYTEwY2IwNjI0YjNhZjAyOTRhMjI0NTc1ZTM3NzczYmVlNzllNDYzZDQ4NmE0MWY1N2MzOWY4ZWVkOTJiNjRjYjJkYjZjYmU0NjEyM2VkZTUwNTMyMTFkNDJlOThlODJkYWM2NjRiOTM4YmI4NjViYjBlNTIyYTc3MTIzNmRmZTMKZmVhZDNmZjFmZjAwNTdmMmU3YzFhYjU3NjhjZTdhZjFiMTZjMzBhZmI0NTNlNDdhYzFhY2QzNjc3NzM5OWQzODI1M2Q0YmZhOTVmNTM2YzI5NmRlYWY4NGMyNzY0N2NkYjIxYzQyZWNmMDM2MTg2MzU2YmMwYmIyYjk0ZTQ4OTEKZWJjMDY5N2RhYzMyNTliNjg4NWUwMGY4NmFjYmRkYWRmNTZkOTAxMDllZTU5NGIyY2NjY2M5MjU5OTZkYmUwYjNjMDJkNDFmMzYyMGI4NDA2MDc4MmNhNmQ5OTk2NjdjNjU5ZTA2MjEzNGYyMzExMTBjMmI2NTk5OTk2NmRmZTgKMGMzMGRiZTA2MTg2ZGJmZmM0MDAyNTExMDEwMTAxMDAwMjAzMDAwMTA0MDIwMzAwMDAwMDAwMDAwMTAwMTExMDMxMjE0MTUxYTEyMDYxODE5MWIxZDFjMWUxZjFmZmRhMDAwODAxMDIwMTAxM2YxMGVlZGUwOTBmODZjZGUxODgKZjUyMjk5M2RjYzhlNjkyYmI5YzE2ZGYxMmNmMTk2NTkyNzBmZTMzYzg3NzE1ZTE3ODQwOWU2ZGFmODFmZjVjMDZiYjYwMzZkMDNkMzZiZWNmYjZjNzQ2MGZiYWM1ODcwZjE5NjU5MjQ5MjcwNTgyNzRjNzA5YjhmZGZmODhjNjEKMWY2YmE2NjRlOGQ1ODU5OWU3MmNiMmM5MjQ5YjRmM2ZhMjBlMDVmZDk5ZmRmZjAwZDQyMTEwY2YwODAwOWM1ZTNmZDkxYjJkYjc4MGIyY2IyYzkyNjFmMWMyNmNjZTA4ZTExODg5N2NmMDFjMzcyN2U0NWUyZDljZjMyYTFlOWIKNjIwODIwYjJjYjJjOTI3NDg0MmU5MjQ2M2MzMGJmODZlMTQ0MTI1YTMxZWFjMTBlMmViYmIyMGZiMTEwNDE2NzE5NjQ5Njk3NzJlZDdhNDViODk2NDJmZGUxODM5MGU5NzkwZjAwYmNlOWQzMDcwMTA3MTk2NTkyNWRlYzJkM2QKNDY1YWNhMjZjNDBmZTUyNjFiMmUxNWEwZGM5ZjAwYjA5ODIzNzI1MmM2MjE4NjM4YzI1NTg0Y2IxYjEzM2JiMTE2YzNhN2RiNjc5NGRmZjNlNjFhY2I2Nzg5MzEyYmU3YjAwNzk2Y2JkZDlmNmYxYzA5ZTU4NmU5ZDQxZWFjNWIKM2Q2NWUxZTUxMTUzNDc4Njc4MzNhM2Y2OWNlZTVmMGNhZjU3NWFjY2ZkOTZiYWRiY2JiODJkNTlkM2IwZTZjMWMzZjkwZmExMmY5OTE0YTYwZGQxYmM5MzVlYWQxZWVjZWRmYjI2YjVmNmJjMDI2ZGYwYzJiODJhMTk2OTg1ODUKOTBlNTkxOWE1OTExMjEzZGU5ODg4ODA3NmNhYmI2MDNhOGI1OTlmZDVlNGUxMzYxOTM5MzFkM2FiYzcwMjYxNDNmNDZjOTFhOTNjMTk3YWYwMWY2ZmYwMDhmZjcxZWU3ZjI1ODc2ZmU0YjA3YjNmYmJjMzhmZTU2NWVjZTUzODQKODk1Zjc3ODk4MmNmYzVlYTU4YzJmNThiY2FjMWUwYmFjZjUxZjY4ZmIyMmQ4YzNmZjc1ZmJiZjk4MmY3ZjliNTZhZmU2M2FmOTI3ZDMwZmE5ZWM0M2Y0ZGZiYWQyNzFlOTk5NzI1ZDhiMzIwZDljZWUxNzYzNmE3ZWUwYmRjZmYKMDAzNmJkNGJlZGM2YzZhNzFkZjA1ZTljOWQ0NmY2MjNlYTJlNGMxNmM4NDQ3OGViODUyZDhlYzZjNDJhOWRhN2UyNTFkZjFiZmEzMjZjZTA2NDJmNzYyNzE2NDViYzFlMjM1Yzc4YjNlNDNmNjI2OGNiNGE0ODljNmZlODc4Y2UKMDUyNjcwMzFjOGYxYmNlYjBjMzYwYzEyNzFiNmYxOTA0NGZmYzQwMDI2MTAwMTAwMDIwMjAxMDMwNTAwMDIwMzAwMDAwMDAwMDAwMDAxMDAxMTIxMzE0MTUxNjE3MTEwODE5MWExYjFjMWUxZDFmMGYxZmZkYTAwMDgwMTAxMDAKMDEzZjEwNmE3MzA4MWQ2NTIwNmQ3NTRmOWU1MjhmNmEzMWFhOGEyNDE3ZDI1MDQ5ZWYwYzU0MTUwY2Y3NjBkOWI4ZDNkYTY5ZjEyZmY0OTdhMTA4MzA2MWIxZGM0ZWEyYTFiYWJkYzdkNjBlMWU1Zjc4Yjk2OTk1NWJiOGMxN2MKYWNiMmI5MWYxMzRmZDIxZTcxMDAxNmZiMjAwYTNlMzg1MzgzZjY5YjNmODUxN2I1M2Y2NDdiMGExZTg0YjBiOGIwNTk5MDFhOWIwYjJlODE3ZWEwYmVhNDIwZjU4YTRjMzgyMzQ4NmRmYzhmN2I2YjY1NjExYzE2ZTI4MmFkNDEKMTk5NDA3NzZhM2ZiMjVhMzA2MjE4ZThhNzk1NTQxMWE3YmExYTA2Y2E2ZDdmMDRkMThkNGUwODUyYWM2ODhiZDA3ZDQ0M2QyZTdhMDgwNzVhYzBmMmQxZjhjYTJjZDQ2ZDQ1Y2IyMGViMGVhMmNmOGM2N2Y4OTYyNzkxN2JlYmUKYWEwYjE4NzA0MjU4NWQyMTg2YTVkYzJjMDVlNjUyYzI4OTcxYzE1MmUzODU4NjExMTM2NDY1ZjQxOTIxOTk5OTk4MzJlMGMyYjlhYzRjN2E0OWNlYWJmYmI5NTBiOGZiNjI4NzQ5NDYxNzQ3OTk2M2YyNTc0ODAwZjY4YWM5NTQKMDk3ODNhNjA1ZjAzOTA3MTEyMzk5NDI5YTg1YjEyNTY1YTYxNWU2Y2U2MzBjMzZmNDFhOTVhOGIyZTVjMTk0YTNhNmZkNDE0MDAwZjU1YjliYjBiMTk4MTYxM2FjYzY1ZGIyOWRlODZmNjdmODNlZmQxNmM0YTQ4MjIwOTUwZjUKYTU1NDA5OWI1OTcyNDU1MzcxMjk4YzE3ODFlMTgwMDQwMzY3MzBiYzFiZjU1OTcyZTVjYjk0NDNhN2RlNTYwOTg4NDFjYzVkNzk5MTFlOTFmNGU2YWNjNWFhMDNmOTBmZTdhNTRhODQ2MTE4MmQ4OWI2NWNlODZjODhhYzMwYzgKOTYyNzEwYWM0MmE1NzI5YzNlODY5MmUyZTIyY2JmNTFhMGIwY2NkOTU0MmNhZGMwYzQwMzZjY2E0MmE0NGE3YzRiY2Q2YzFiN2VhZTU0NjBjZjZiODFmMTZmNzljMjhjMTg4NzBkNTQ2YmQyM2FjZGNjYjEzMzA3YzU5YWI5NDIKNDJkZjE4Y2IyZjE4MjBkZTI5ZTYxODYxMjIzMDNkYzA5YzY1YmMyM2Q2OTQ3MTgzMjgyYjJmNDUxMDg0ZTJhNWY4YTlmNzZlMmIzNWQ2YWUzOGI0ZDVlMDRhZmNlNjU1YjYxYWU1MTNmMGZkOTY3ZTU1NzVkNDUzZjYzMTUxNmYKYTA2ZTJiYThlNDA1ZTI5OGU4MzRiZTYwNzU4ZDExYjg1MzEwZTM3MTBiMmExMDZkNDhhOGI1Y2U1MDkxMDE3ODIwODUwYmQ1ODRhZjM3YTAzODI1MGM5ZTU5NTk2ZjcyNmVhNjhmOGNlNjZkZDY1NjA1OGE3NDk3YjM2ZDQwNmUKMWE0NWE5NTcyODIyNmJkNzIxY2MxYzQxNWQ4YTU4MjUwNTkzMDJiMDIyZTM3NjFkMDM4N2JjNTJlMjU5NGFiNThiNTE1MzFhOTUzMGY3OGQ0YTA4MjMyYzE1ZmI5MTEwMTc0NGIyMThiNzg4NTQwZjdhMTgxZTY4ZjUwZjk2MWIKYmY2NjI2MjFmYWQxNjJiYjgyNzU0NGM1YWU1MjFmMWExYTVlYTA5MmQ0YTRlYTQ3MTQzMWQyNTllOWY4MGNjNDE1MTcyZDZhMTZlNTAxYTU5NzNmM2E5OTJiNzgwNGI0NTQ2NTcwYTJlMmNmZDkwY2UxZTRlMmM0YmY4YjlhOTcKOGFhZGY3ODVkZDZiYjEyOTg1ZjQ1ZTIxNTM1ZjJjM2MwOGYwYzc3NThmOTRlMmJmMTJlZDUwZWUxMzFjNTVkODk3ZTMwOGJiMWZkNGI2ZmNmNjU1MjgxNzZiZGUwN2FmOWE1ZTUwMWVkMGNlMDY4ZTEyMmNlZWQ5ZjgzNjc5ZDQKNmIwNDg0MDk0ZWNkZTYyMTUzMDBhZDQxZDdmYWM0YzkzM2I2NzgxYjhhNDJmMjE3ZTA0NjI1OGFhY2RiZTQ5NmUyMGJiMWI2NzJkMzg4ZTYyMDU0MDAxY2RhOTVkZTUxNTExNTU4ZTM3NDg2NjVjYThjZjQ1ODE5NjljZWQ0ZjYKOTY1OTBmNzhhNmQ1NzkyZTNmYzExOTQ2OGFmYTg5YjlkOGI2NDhiYjY3NGRhMDBkOTVlMGI4YWI4NjkxODhmNGJkNDBmZGY2ODAwM2U2MWQzNDYwYTJiNTlhOGFkMThjYTZmZDkzMGNjYmRjN2U4OGE2M2M5NWZjY2I3NGJjMGIKZjYxNTY0NzVjMmNkNDg5OTFiMTc1YjkwZWRiYWRiNzFlMDA4MTU4MWMxNTc0Mjc5NDEwOTM0MWIwNWU1ZDA3YjBjYmU5N2I1ZmYwMGI1OTVlZDUzNzIyZTA5Nzk4YmM2M2YwMDkwYjE4MGY2MjUwZDA4ZTgyY2IwNjQzY2U2NzAKZDE4Y2I3YzI5Y2MyM2IxMmI2NTdkZTM5NzU4ZTM0NGM4MTAzYTA3MWU2MTc3NWExZmYwMGQ1OTlhNDQwMWRkMzZlNGVkMDcwMDk1ZjExMzM1MGYxMjc1YmY5MmE5MTU2YmZlNzI4YjFlZjdmYzQxZjQ3YjdmYTRjYTAxZWY3ZmUKMTAwY2ZlMTJmZjAwNmRhNzVmODg5NzBiNGVhMmZjODUyYTRlODhmYjg2YTc0NjZkNjIyYmU1MjFiZTVhYjk4NTIzYWM3ZjBiMjE2NDhlZDRlNTJhNTkxY2QyZDgwNWY5OTY0Y2E4ZTBiODNlNTAzZDc5OTY2ZjUwZGMwYmQyNmYKYTgzZDFhOTY4ODVlYzZmODk3OGI4YjE2OTEyMjQ4ZDZiMTEyMmFiYzQzNGRmZDA5NWE0OThlOGM1NGVlODY0YTNiZDQ1N2IxMTZmMDllZDJmYjIxYzU0YWIxNjgzNTBmZjEyYjI3Y2ViZWE1M2Q4MTA1MTA0Mzk3NTMwMGJiNTEKOTk5MzQ1MDkyYzg0ZjI4MzMwYzc1ODc2NDM1ZWI4MjYwYmMwMWU2MDVjMmUxODMwNDRiOGE2MDQ1OGM1NzJlNjNjMTNkYTM1M2RjNDAzMDk3YzRhNzYxMDc4ODNjMTAyMWEyMmU2NjExNzkwOTkyNzUzYzg5NmU0NGNmNWFiYTkKMWZhMjE2MDk0OTA0MjM2ZjMxODQ2ZTYyYmE4NjU3MDRlNDg5YzkxMGI1MTI5OWM0YWY1MzQ4NDU4YzQ0OTg4NWYyNDY1MDViNDI1MWZjODgxYjA5MDczODc3ODIxMGM0MTA3MGQ0NGJjNDVlNjM4NjQ4M2Q2NzcyNjMxNTFjMTYKNDFlZDBlYjEwYzM2NDY2YTBlZDEyNTkxNjZhMDEzMDk0ZDg4YTZjODNlMjEwYzRiMmM2ZTEyZTY3MDQwOTkyNTg1ODI2NDYyYTNhYzVhODRiODk1MjdmZmQ5fX19fX0KClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjV7XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMlxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjhcaGljaFxhZjgKRnVzY2Ugdml0YWUgdmVzdGlidWx1bSB2ZWxpdC4gUGVsbGVudGVzcXVlIHZ1bHB1dGF0ZSBsZWN0dXMgcXVpcyBwZWxsZW50ZXNxdWUgY29tbW9kby4gQWxpcXVhbSBlcmF0IHZvbHV0cGF0LiBWZXN0aWJ1bHVtIGluIGVnZXN0YXMgdmVsaXQuIFBlbGxlbnRlc3F1ZSBmZXJtZW50dW0gbmlzbCB2aXRhZSBmcmluZ2lsbGEgdmVuZW5hdGlzLiBFdGlhbSBpZCBtYXVyaXMgdml0YWUgb3JjaSBtYXhpbXVzIHVsdHJpY2llcy4gQ3JhcyBmcmluZ2lsbGEgaXBzdW0gbWFnbmEsIGluIGZyaW5naWxsYSBkdWkgY29tbW9kbyBhLn0KXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjEyXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmOFxoaWNoXGFmOApFdGlhbSB2ZWhpY3VsYSBsdWN0dXMgZmVybWVudHVtLiBJbiB2ZWwgbWV0dXMgY29uZ3VlLCBwdWx2aW5hciBsZWN0dXMgdmVsLCBmZXJtZW50dW0gZHVpLiBNYWVjZW5hcyBhbnRlIG9yY2ksIGVnZXN0YXMgdXQgYWxpcXVldCBzaXQgYW1ldCwgc2FnaXR0aXMgYSBtYWduYS4gQWxpcXVhbSBhbnRlIHF1YW0sIHBlbGxlbnRlc3F1ZSB1dCBkaWduaXNzaW0gcXVpcywgbGFvcmVldCBlZ2V0IGVzdC4gQWxpcXVhbSBlcmF0IHZvbHV0cGF0LiBDbGFzcyBhcHRlbnQgdGFjaXRpIHNvY2lvc3F1IGFkIGxpdG9yYSB0b3JxdWVudCBwZXIgY29udWJpYSBub3N0cmEsIHBlciBpbmNlcHRvcyBoaW1lbmFlb3MuIFV0IHVsbGFtY29ycGVyIGp1c3RvIHNhcGllbiwgaW4gY3Vyc3VzIGxpYmVybyB2aXZlcnJhIGVnZXQuIFZpdmFtdXMgYXVjdG9yIGltcGVyZGlldCB1cm5hLCBhdCBwdWx2aW5hciBsZW8gcG9zdWVyZSBsYW9yZWV0LiBTdXNwZW5kaXNzZSBuZXF1ZSBuaXNsLCBmcmluZ2lsbGEgYXQgaWFjdWxpcyBzY2VsZXJpc3F1ZSwgb3JuYXJlIHZlbCBkb2xvci4gVXQgZXQgcHVsdmluYXIgbnVuYy4gUGVsbGVudGVzcXVlIGZyaW5naWxsYSBtb2xsaXMgZWZmaWNpdHVyLiBOdWxsYW0gdmVuZW5hdGlzIGNvbW1vZG8gaW1wZXJkaWV0LiBNb3JiaSB2ZWxpdCBuZXF1ZSwgc2VtcGVyIHF1aXMgbG9yZW0gcXVpcywgZWZmaWNpdHVyIGRpZ25pc3NpbSBpcHN1bS4gVXQgYWMgbG9yZW0gc2VkIHR1cnBpcyBpbXBlcmRpZXQgZWxlaWZlbmQgc2l0IGFtZXQgaWQgc2FwaWVuLn0KXHBhciBccGFyZFxwbGFpbiBcczFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IyNDBcc2ExMjBca2VlcG5cbHRycGFyXGNmMTdcYlxkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMzNlxhbGFuZzEwODFcYWJcbG9jaFxmNFxmczM2XGxhbmcxMDMze1xsaXN0dGV4dFxwYXJkXHBsYWluIH1caWx2bDBcbHMxIFxsaTc5MlxyaTBcbGluNzkyXHJpbjBcZmktNDMye1xydGxjaCBcbHRyY2hcbG9jaApMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiB9ClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjEyXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmOFxoaWNoXGFmOAoKXHBhciBccGFyZFxwbGFpbiBcczUxXHNsMjg4XHNsbXVsdDFccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IwXHNhMTQwXGx0cnBhclxjZjE3XGRiY2hcYWY5XGxhbmdmZTIwNTJcZGJjaFxhZjEzXGFmczI0XGFsYW5nMTA4MVxsb2NoXGYzXGZzMjRcbGFuZzEwMzNccWpcd2lkY3RscGFyXHNiMFxzYTIyNXtcc2NhcHMwXGNhcHMwXGNmMVxleHBuZDBcZXhwbmR0dzBcaTBcYjBcZGJjaFxhZjEyXHJ0bGNoIFxsdHJjaFxsb2NoXGZzMjFcbG9jaFxmOFxoaWNoXGFmOApOdW5jIGFjIGZhdWNpYnVzIG9kaW8uIFZlc3RpYnVsdW0gbmVxdWUgbWFzc2EsIHNjZWxlcmlzcXVlIHNpdCBhbWV0IGxpZ3VsYSBldSwgY29uZ3VlIG1vbGVzdGllIG1pLiBQcmFlc2VudCB1dCB2YXJpdXMgc2VtLiBOdWxsYW0gYXQgcG9ydHRpdG9yIGFyY3UsIG5lYyBsYWNpbmlhIG5pc2kuIFV0IGFjIGRvbG9yIHZpdGFlIG9kaW8gaW50ZXJkdW0gY29uZGltZW50dW0uIFZpdmFtdXMgZGFwaWJ1cyBzb2RhbGVzIGV4LCB2aXRhZSBtYWxlc3VhZGEgaXBzdW0gY3Vyc3VzIGNvbnZhbGxpcy4gTWFlY2VuYXMgc2VkIGVnZXN0YXMgbnVsbGEsIGFjIGNvbmRpbWVudHVtIG9yY2kuIE1hdXJpcyBkaWFtIGZlbGlzLCB2dWxwdXRhdGUgYWMgc3VzY2lwaXQgZXQsIGlhY3VsaXMgbm9uIGVzdC4gQ3VyYWJpdHVyIHNlbXBlciBhcmN1IGFjIGxpZ3VsYSBzZW1wZXIsIG5lYyBsdWN0dXMgbmlzbCBibGFuZGl0LiBJbnRlZ2VyIGxhY2luaWEgYW50ZSBhYyBsaWJlcm8gbG9ib3J0aXMgaW1wZXJkaWV0LiBOdWxsYW0gbW9sbGlzIGNvbnZhbGxpcyBpcHN1bSwgYWMgYWNjdW1zYW4gbnVuYyB2ZWhpY3VsYSB2aXRhZS4gTnVsbGEgZWdldCBqdXN0byBpbiBmZWxpcyB0cmlzdGlxdWUgZnJpbmdpbGxhLiBNb3JiaSBzaXQgYW1ldCB0b3J0b3IgcXVpcyByaXN1cyBhdWN0b3IgY29uZGltZW50dW0uIE1vcmJpIGluIHVsbGFtY29ycGVyIGVsaXQuIE51bGxhIGlhY3VsaXMgdGVsbHVzIHNpdCBhbWV0IG1hdXJpcyB0ZW1wdXMgZnJpbmdpbGxhLn0KXHBhciBccGFyZFxwbGFpbiBcczJccWxcbm93aWRjdGxwYXJcaHlwaHBhcjBcc2IyMDBcc2ExMjBca2VlcG5cbHRycGFyXGNmMTdcYlxkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMzMlxhbGFuZzEwODFcYWJcbG9jaFxmNFxmczMyXGxhbmcxMDMze1xsaXN0dGV4dFxwYXJkXHBsYWluIH1caWx2bDFcbHMxIFxsaTkzNlxyaTBcbGluOTM2XHJpbjBcZmktNTc2e1xydGxjaCBcbHRyY2hcbG9jaApNYWVjZW5hcyBtYXVyaXMgbGVjdHVzLCBsb2JvcnRpcyBldCBwdXJ1cyBtYXR0aXMsIGJsYW5kaXQgZGljdHVtIHRlbGx1cy4gfQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1e1xzY2FwczBcY2FwczBcY2YxXGV4cG5kMFxleHBuZHR3MFxpMFxiMFxkYmNoXGFmMTJccnRsY2ggXGx0cmNoXGxvY2hcZnMyMVxsb2NoXGY4XGhpY2hcYWY4Ck1hZWNlbmFzIG5vbiBsb3JlbSBxdWlzIHRlbGx1cyBwbGFjZXJhdCB2YXJpdXMuIE51bGxhIGZhY2lsaXNpLiBBZW5lYW4gY29uZ3VlIGZyaW5naWxsYSBqdXN0byB1dCBhbGlxdWFtLiBNYXVyaXMgaWQgZXggZXJhdC4gTnVuYyB2dWxwdXRhdGUgbmVxdWUgdml0YWUganVzdG8gZmFjaWxpc2lzLCBub24gY29uZGltZW50dW0gYW50ZSBzYWdpdHRpcy4gTW9yYmkgdml2ZXJyYSBzZW1wZXIgbG9yZW0gbmVjIG1vbGVzdGllLiBNYWVjZW5hcyB0aW5jaWR1bnQgZXN0IGVmZmljaXR1ciBsaWd1bGEgZXVpc21vZCwgc2l0IGFtZXQgb3JuYXJlIGVzdCB2dWxwdXRhdGUufQpccGFyIFxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1XHJ0bGNoIFxsdHJjaFxsb2NoCgpccGFyIFxzaHB3cjJcc2hwd3JrM1xzaHBieXBhcmFcc2hwYnlpZ25vcmVcc2hwdG9wMFxzaHBieGNvbHVtblxzaHBieGlnbm9yZVxzaHBsZWZ0MjgxOVxwYXJkXHBsYWluIFxzNTFcc2wyODhcc2xtdWx0MVxxbFxub3dpZGN0bHBhclxoeXBocGFyMFxzYjBcc2ExNDBcbHRycGFyXGNmMTdcZGJjaFxhZjlcbGFuZ2ZlMjA1MlxkYmNoXGFmMTNcYWZzMjRcYWxhbmcxMDgxXGxvY2hcZjNcZnMyNFxsYW5nMTAzM1xxalx3aWRjdGxwYXJcc2IwXHNhMjI1XHJ0bGNoIFxsdHJjaFxsb2NoCntcKlxmbHltYWluY250NVxmbHlhbmNob3IwXGZseWNudG50fXtcc2hwe1wqXHNocGluc3Rcc2hwd3IyXHNocHdyazNcc2hwYnlwYXJhXHNocGJ5aWdub3JlXHNocHRvcDBcc2hwYm90dG9tMjY2MFxzaHBieGNvbHVtblxzaHBieGlnbm9yZVxzaHBsZWZ0MjgxOVxzaHByaWdodDY4MTl7XHNwe1xzbiBzaGFwZVR5cGV9e1xzdiA3NX19e1xzcHtcc24gd3pEZXNjcmlwdGlvbn17XHN2IH19e1xzcHtcc24gd3pOYW1lfXtcc3YgfX17XHNwe1xzbiBwaWJ9e1xzdiB7XHBpY3RccGljc2NhbGV4MTAwXHBpY3NjYWxleTEwMFxwaWNjcm9wbDBccGljY3JvcHIwXHBpY2Nyb3B0MFxwaWNjcm9wYjBccGljdzIwMFxwaWNoMTMzXHBpY3dnb2FsNDAwMFxwaWNoZ29hbDI2NjBcanBlZ2JsaXAKZmZkOGZmZTAwMDEwNGE0NjQ5NDYwMDAxMDEwMTAwNDgwMDQ4MDAwMGZmZTIwYzU4NDk0MzQzNWY1MDUyNGY0NjQ5NGM0NTAwMDEwMTAwMDAwYzQ4NGM2OTZlNmYwMjEwMDAwMDZkNmU3NDcyNTI0NzQyMjA1ODU5NWEyMDA3Y2UKMDAwMjAwMDkwMDA2MDAzMTAwMDA2MTYzNzM3MDRkNTM0NjU0MDAwMDAwMDA0OTQ1NDMyMDczNTI0NzQyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGY2ZDYwMDAxMDAwMDAwMDBkMzJkNDg1MDIwMjAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDExNjM3MDcyNzQwMDAwMDE1MDAwMDAwMDMzNjQ2NTczNjMwMDAwMDE4NDAwMDAKMDA2Yzc3NzQ3MDc0MDAwMDAxZjAwMDAwMDAxNDYyNmI3MDc0MDAwMDAyMDQwMDAwMDAxNDcyNTg1OTVhMDAwMDAyMTgwMDAwMDAxNDY3NTg1OTVhMDAwMDAyMmMwMDAwMDAxNDYyNTg1OTVhMDAwMDAyNDAwMDAwMDAxNDY0NmQKNmU2NDAwMDAwMjU0MDAwMDAwNzA2NDZkNjQ2NDAwMDAwMmM0MDAwMDAwODg3Njc1NjU2NDAwMDAwMzRjMDAwMDAwODY3NjY5NjU3NzAwMDAwM2Q0MDAwMDAwMjQ2Yzc1NmQ2OTAwMDAwM2Y4MDAwMDAwMTQ2ZDY1NjE3MzAwMDAKMDQwYzAwMDAwMDI0NzQ2NTYzNjgwMDAwMDQzMDAwMDAwMDBjNzI1NDUyNDMwMDAwMDQzYzAwMDAwODBjNjc1NDUyNDMwMDAwMDQzYzAwMDAwODBjNjI1NDUyNDMwMDAwMDQzYzAwMDAwODBjNzQ2NTc4NzQwMDAwMDAwMDQzNmYKNzA3OTcyNjk2NzY4NzQyMDI4NjMyOTIwMzEzOTM5MzgyMDQ4NjU3NzZjNjU3NDc0MmQ1MDYxNjM2YjYxNzI2NDIwNDM2ZjZkNzA2MTZlNzkwMDAwNjQ2NTczNjMwMDAwMDAwMDAwMDAwMDEyNzM1MjQ3NDIyMDQ5NDU0MzM2MzEKMzkzNjM2MmQzMjJlMzEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTI3MzUyNDc0MjIwNDk0NTQzMzYzMTM5MzYzNjJkMzIyZTMxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA1ODU5NWEyMDAwMDAwMDAwMDAwMGYzNTEwMDAxMDAwMDAwMDExNmNjNTg1OTVhMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDU4NTkKNWEyMDAwMDAwMDAwMDAwMDZmYTIwMDAwMzhmNTAwMDAwMzkwNTg1OTVhMjAwMDAwMDAwMDAwMDA2Mjk5MDAwMGI3ODUwMDAwMThkYTU4NTk1YTIwMDAwMDAwMDAwMDAwMjRhMDAwMDAwZjg0MDAwMGI2Y2Y2NDY1NzM2MzAwMDAKMDAwMDAwMDAwMDE2NDk0NTQzMjA2ODc0NzQ3MDNhMmYyZjc3Nzc3NzJlNjk2NTYzMmU2MzY4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE2NDk0NTQzMjA2ODc0NzQ3MDNhMmYyZjc3Nzc3NzJlNjk2NTYzMmU2MzY4MDAwMDAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjQ2NTczNjMwMDAwMDAwMDAwMDAwMDJlNDk0NTQzMjAzNjMxMzkzNjM2MmQKMzIyZTMxMjA0NDY1NjY2MTc1NmM3NDIwNTI0NzQyMjA2MzZmNmM2Zjc1NzIyMDczNzA2MTYzNjUyMDJkMjA3MzUyNDc0MjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyZTQ5NDU0MzIwMzYzMTM5MzYzNjJkMzIyZTMxMjA0NDY1NjYKNjE3NTZjNzQyMDUyNDc0MjIwNjM2ZjZjNmY3NTcyMjA3MzcwNjE2MzY1MjAyZDIwNzM1MjQ3NDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDY0NjU3MzYzMDAwMDAwMDAwMDAwMDAyYzUyNjUKNjY2NTcyNjU2ZTYzNjUyMDU2Njk2NTc3Njk2ZTY3MjA0MzZmNmU2NDY5NzQ2OTZmNmUyMDY5NmUyMDQ5NDU0MzM2MzEzOTM2MzYyZDMyMmUzMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyYzUyNjU2NjY1NzI2NTZlNjM2NTIwNTYKNjk2NTc3Njk2ZTY3MjA0MzZmNmU2NDY5NzQ2OTZmNmUyMDY5NmUyMDQ5NDU0MzM2MzEzOTM2MzYyZDMyMmUzMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA3NjY5NjU3NzAwMDAKMDAwMDAwMTNhNGZlMDAxNDVmMmUwMDEwY2YxNDAwMDNlZGNjMDAwNDEzMGIwMDAzNWM5ZTAwMDAwMDAxNTg1OTVhMjAwMDAwMDAwMDAwNGMwOTU2MDA1MDAwMDAwMDU3MWZlNzZkNjU2MTczMDAwMDAwMDAwMDAwMDAwMTAwMDAKMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjhmMDAwMDAwMDI3MzY5NjcyMDAwMDAwMDAwNDM1MjU0MjA2Mzc1NzI3NjAwMDAwMDAwMDAwMDA0MDAwMDAwMDAwNTAwMGEwMDBmMDAxNDAwMTkwMDFlMDAyMzAwMjgKMDAyZDAwMzIwMDM3MDAzYjAwNDAwMDQ1MDA0YTAwNGYwMDU0MDA1OTAwNWUwMDYzMDA2ODAwNmQwMDcyMDA3NzAwN2MwMDgxMDA4NjAwOGIwMDkwMDA5NTAwOWEwMDlmMDBhNDAwYTkwMGFlMDBiMjAwYjcwMGJjMDBjMTAwYzYKMDBjYjAwZDAwMGQ1MDBkYjAwZTAwMGU1MDBlYjAwZjAwMGY2MDBmYjAxMDEwMTA3MDEwZDAxMTMwMTE5MDExZjAxMjUwMTJiMDEzMjAxMzgwMTNlMDE0NTAxNGMwMTUyMDE1OTAxNjAwMTY3MDE2ZTAxNzUwMTdjMDE4MzAxOGIKMDE5MjAxOWEwMWExMDFhOTAxYjEwMWI5MDFjMTAxYzkwMWQxMDFkOTAxZTEwMWU5MDFmMjAxZmEwMjAzMDIwYzAyMTQwMjFkMDIyNjAyMmYwMjM4MDI0MTAyNGIwMjU0MDI1ZDAyNjcwMjcxMDI3YTAyODQwMjhlMDI5ODAyYTIKMDJhYzAyYjYwMmMxMDJjYjAyZDUwMmUwMDJlYjAyZjUwMzAwMDMwYjAzMTYwMzIxMDMyZDAzMzgwMzQzMDM0ZjAzNWEwMzY2MDM3MjAzN2UwMzhhMDM5NjAzYTIwM2FlMDNiYTAzYzcwM2QzMDNlMDAzZWMwM2Y5MDQwNjA0MTMKMDQyMDA0MmQwNDNiMDQ0ODA0NTUwNDYzMDQ3MTA0N2UwNDhjMDQ5YTA0YTgwNGI2MDRjNDA0ZDMwNGUxMDRmMDA0ZmUwNTBkMDUxYzA1MmIwNTNhMDU0OTA1NTgwNTY3MDU3NzA1ODYwNTk2MDVhNjA1YjUwNWM1MDVkNTA1ZTUKMDVmNjA2MDYwNjE2MDYyNzA2MzcwNjQ4MDY1OTA2NmEwNjdiMDY4YzA2OWQwNmFmMDZjMDA2ZDEwNmUzMDZmNTA3MDcwNzE5MDcyYjA3M2QwNzRmMDc2MTA3NzQwNzg2MDc5OTA3YWMwN2JmMDdkMjA3ZTUwN2Y4MDgwYjA4MWYKMDgzMjA4NDYwODVhMDg2ZTA4ODIwODk2MDhhYTA4YmUwOGQyMDhlNzA4ZmIwOTEwMDkyNTA5M2EwOTRmMDk2NDA5NzkwOThmMDlhNDA5YmEwOWNmMDllNTA5ZmIwYTExMGEyNzBhM2QwYTU0MGE2YTBhODEwYTk4MGFhZTBhYzUKMGFkYzBhZjMwYjBiMGIyMjBiMzkwYjUxMGI2OTBiODAwYjk4MGJiMDBiYzgwYmUxMGJmOTBjMTIwYzJhMGM0MzBjNWMwYzc1MGM4ZTBjYTcwY2MwMGNkOTBjZjMwZDBkMGQyNjBkNDAwZDVhMGQ3NDBkOGUwZGE5MGRjMzBkZGUKMGRmODBlMTMwZTJlMGU0OTBlNjQwZTdmMGU5YjBlYjYwZWQyMGVlZTBmMDkwZjI1MGY0MTBmNWUwZjdhMGY5NjBmYjMwZmNmMGZlYzEwMDkxMDI2MTA0MzEwNjExMDdlMTA5YjEwYjkxMGQ3MTBmNTExMTMxMTMxMTE0ZjExNmQKMTE4YzExYWExMWM5MTFlODEyMDcxMjI2MTI0NTEyNjQxMjg0MTJhMzEyYzMxMmUzMTMwMzEzMjMxMzQzMTM2MzEzODMxM2E0MTNjNTEzZTUxNDA2MTQyNzE0NDkxNDZhMTQ4YjE0YWQxNGNlMTRmMDE1MTIxNTM0MTU1NjE1NzgKMTU5YjE1YmQxNWUwMTYwMzE2MjYxNjQ5MTY2YzE2OGYxNmIyMTZkNjE2ZmExNzFkMTc0MTE3NjUxNzg5MTdhZTE3ZDIxN2Y3MTgxYjE4NDAxODY1MTg4YTE4YWYxOGQ1MThmYTE5MjAxOTQ1MTk2YjE5OTExOWI3MTlkZDFhMDQKMWEyYTFhNTExYTc3MWE5ZTFhYzUxYWVjMWIxNDFiM2IxYjYzMWI4YTFiYjIxYmRhMWMwMjFjMmExYzUyMWM3YjFjYTMxY2NjMWNmNTFkMWUxZDQ3MWQ3MDFkOTkxZGMzMWRlYzFlMTYxZTQwMWU2YTFlOTQxZWJlMWVlOTFmMTMKMWYzZTFmNjkxZjk0MWZiZjFmZWEyMDE1MjA0MTIwNmMyMDk4MjBjNDIwZjAyMTFjMjE0ODIxNzUyMWExMjFjZTIxZmIyMjI3MjI1NTIyODIyMmFmMjJkZDIzMGEyMzM4MjM2NjIzOTQyM2MyMjNmMDI0MWYyNDRkMjQ3YzI0YWIKMjRkYTI1MDkyNTM4MjU2ODI1OTcyNWM3MjVmNzI2MjcyNjU3MjY4NzI2YjcyNmU4MjcxODI3NDkyNzdhMjdhYjI3ZGMyODBkMjgzZjI4NzEyOGEyMjhkNDI5MDYyOTM4Mjk2YjI5OWQyOWQwMmEwMjJhMzUyYTY4MmE5YjJhY2YKMmIwMjJiMzYyYjY5MmI5ZDJiZDEyYzA1MmMzOTJjNmUyY2EyMmNkNzJkMGMyZDQxMmQ3NjJkYWIyZGUxMmUxNjJlNGMyZTgyMmViNzJlZWUyZjI0MmY1YTJmOTEyZmM3MmZmZTMwMzUzMDZjMzBhNDMwZGIzMTEyMzE0YTMxODIKMzFiYTMxZjIzMjJhMzI2MzMyOWIzMmQ0MzMwZDMzNDYzMzdmMzNiODMzZjEzNDJiMzQ2NTM0OWUzNGQ4MzUxMzM1NGQzNTg3MzVjMjM1ZmQzNjM3MzY3MjM2YWUzNmU5MzcyNDM3NjAzNzljMzdkNzM4MTQzODUwMzg4YzM4YzgKMzkwNTM5NDIzOTdmMzliYzM5ZjkzYTM2M2E3NDNhYjIzYWVmM2IyZDNiNmIzYmFhM2JlODNjMjczYzY1M2NhNDNjZTMzZDIyM2Q2MTNkYTEzZGUwM2UyMDNlNjAzZWEwM2VlMDNmMjEzZjYxM2ZhMjNmZTI0MDIzNDA2NDQwYTYKNDBlNzQxMjk0MTZhNDFhYzQxZWU0MjMwNDI3MjQyYjU0MmY3NDMzYTQzN2Q0M2MwNDQwMzQ0NDc0NDhhNDRjZTQ1MTI0NTU1NDU5YTQ1ZGU0NjIyNDY2NzQ2YWI0NmYwNDczNTQ3N2I0N2MwNDgwNTQ4NGI0ODkxNDhkNzQ5MWQKNDk2MzQ5YTk0OWYwNGEzNzRhN2Q0YWM0NGIwYzRiNTM0YjlhNGJlMjRjMmE0YzcyNGNiYTRkMDI0ZDRhNGQ5MzRkZGM0ZTI1NGU2ZTRlYjc0ZjAwNGY0OTRmOTM0ZmRkNTAyNzUwNzE1MGJiNTEwNjUxNTA1MTliNTFlNjUyMzEKNTI3YzUyYzc1MzEzNTM1ZjUzYWE1M2Y2NTQ0MjU0OGY1NGRiNTUyODU1NzU1NWMyNTYwZjU2NWM1NmE5NTZmNzU3NDQ1NzkyNTdlMDU4MmY1ODdkNThjYjU5MWE1OTY5NTliODVhMDc1YTU2NWFhNjVhZjU1YjQ1NWI5NTViZTUKNWMzNTVjODY1Y2Q2NWQyNzVkNzg1ZGM5NWUxYTVlNmM1ZWJkNWYwZjVmNjE1ZmIzNjAwNTYwNTc2MGFhNjBmYzYxNGY2MWEyNjFmNTYyNDk2MjljNjJmMDYzNDM2Mzk3NjNlYjY0NDA2NDk0NjRlOTY1M2Q2NTkyNjVlNzY2M2QKNjY5MjY2ZTg2NzNkNjc5MzY3ZTk2ODNmNjg5NjY4ZWM2OTQzNjk5YTY5ZjE2YTQ4NmE5ZjZhZjc2YjRmNmJhNzZiZmY2YzU3NmNhZjZkMDg2ZDYwNmRiOTZlMTI2ZTZiNmVjNDZmMWU2Zjc4NmZkMTcwMmI3MDg2NzBlMDcxM2EKNzE5NTcxZjA3MjRiNzJhNjczMDE3MzVkNzNiODc0MTQ3NDcwNzRjYzc1Mjg3NTg1NzVlMTc2M2U3NjliNzZmODc3NTY3N2IzNzgxMTc4NmU3OGNjNzkyYTc5ODk3OWU3N2E0NjdhYTU3YjA0N2I2MzdiYzI3YzIxN2M4MTdjZTEKN2Q0MTdkYTE3ZTAxN2U2MjdlYzI3ZjIzN2Y4NDdmZTU4MDQ3ODBhODgxMGE4MTZiODFjZDgyMzA4MjkyODJmNDgzNTc4M2JhODQxZDg0ODA4NGUzODU0Nzg1YWI4NjBlODY3Mjg2ZDc4NzNiODc5Zjg4MDQ4ODY5ODhjZTg5MzMKODk5OTg5ZmU4YTY0OGFjYThiMzA4Yjk2OGJmYzhjNjM4Y2NhOGQzMThkOTg4ZGZmOGU2NjhlY2U4ZjM2OGY5ZTkwMDY5MDZlOTBkNjkxM2Y5MWE4OTIxMTkyN2E5MmUzOTM0ZDkzYjY5NDIwOTQ4YTk0ZjQ5NTVmOTVjOTk2MzQKOTY5Zjk3MGE5Nzc1OTdlMDk4NGM5OGI4OTkyNDk5OTA5OWZjOWE2ODlhZDU5YjQyOWJhZjljMWM5Yzg5OWNmNzlkNjQ5ZGQyOWU0MDllYWU5ZjFkOWY4YjlmZmFhMDY5YTBkOGExNDdhMWI2YTIyNmEyOTZhMzA2YTM3NmEzZTYKYTQ1NmE0YzdhNTM4YTVhOWE2MWFhNjhiYTZmZGE3NmVhN2UwYTg1MmE4YzRhOTM3YTlhOWFhMWNhYThmYWIwMmFiNzVhYmU5YWM1Y2FjZDBhZDQ0YWRiOGFlMmRhZWExYWYxNmFmOGJiMDAwYjA3NWIwZWFiMTYwYjFkNmIyNGIKYjJjMmIzMzhiM2FlYjQyNWI0OWNiNTEzYjU4YWI2MDFiNjc5YjZmMGI3NjhiN2UwYjg1OWI4ZDFiOTRhYjljMmJhM2JiYWI1YmIyZWJiYTdiYzIxYmM5YmJkMTViZDhmYmUwYWJlODRiZWZmYmY3YWJmZjVjMDcwYzBlY2MxNjcKYzFlM2MyNWZjMmRiYzM1OGMzZDRjNDUxYzRjZWM1NGJjNWM4YzY0NmM2YzNjNzQxYzdiZmM4M2RjOGJjYzkzYWM5YjljYTM4Y2FiN2NiMzZjYmI2Y2MzNWNjYjVjZDM1Y2RiNWNlMzZjZWI2Y2YzN2NmYjhkMDM5ZDBiYWQxM2MKZDFiZWQyM2ZkMmMxZDM0NGQzYzZkNDQ5ZDRjYmQ1NGVkNWQxZDY1NWQ2ZDhkNzVjZDdlMGQ4NjRkOGU4ZDk2Y2Q5ZjFkYTc2ZGFmYmRiODBkYzA1ZGM4YWRkMTBkZDk2ZGUxY2RlYTJkZjI5ZGZhZmUwMzZlMGJkZTE0NGUxY2MKZTI1M2UyZGJlMzYzZTNlYmU0NzNlNGZjZTU4NGU2MGRlNjk2ZTcxZmU3YTllODMyZThiY2U5NDZlOWQwZWE1YmVhZTVlYjcwZWJmYmVjODZlZDExZWQ5Y2VlMjhlZWI0ZWY0MGVmY2NmMDU4ZjBlNWYxNzJmMWZmZjI4Y2YzMTkKZjNhN2Y0MzRmNGMyZjU1MGY1ZGVmNjZkZjZmYmY3OGFmODE5ZjhhOGY5MzhmOWM3ZmE1N2ZhZTdmYjc3ZmMwN2ZjOThmZDI5ZmRiYWZlNGJmZWRjZmY2ZGZmZmZmZmRiMDA0MzAwMDUwNDA0MDQwNDAzMDUwNDA0MDQwNjA1MDUKMDYwODBkMDgwODA3MDcwODEwMGIwYzA5MGQxMzEwMTQxMzEyMTAxMjEyMTQxNzFkMTkxNDE2MWMxNjEyMTIxYTIzMWExYzFlMWYyMTIxMjExNDE5MjQyNzI0MjAyNjFkMjAyMTIwZmZkYjAwNDMwMTA1MDYwNjA4MDcwODBmMDgKMDgwZjIwMTUxMjE1MjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMGZmYzIwMDExMDgwMDg1MDAKYzgwMzAxMTEwMDAyMTEwMTAzMTEwMWZmYzQwMDFjMDAwMDAxMDUwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAwMTAzMDQwNTA2MDcwOGZmYzQwMDFhMDEwMDAzMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDIKMDMwMDA0MDUwNmZmZGEwMDBjMDMwMTAwMDIxMDAzMTAwMDAwMDFjZWY5MGZiMDlhNzI5NTJkNmE3NWNiZWE0ZDRlNjNjOGZiNWU0NmI0N2I3YWMxM2VlZmJiYzYwMGQxYWIwMDc2MDU2Y2UwMTAwNDE2YWNhZmNmNzJmNzY0YzUKYjJjODhjYjc3NzVlM2RkYTcyNjRmNTc1NGVkMzM2MGRiMDM2MDFhYTExMDJkNmM4MWJjZmNhMzk4NDExYzU2Y2UzMmQ5ZDQxMDFjODQ3YmI4MzRlOGJiY2VhODg5MTZiZWFkZDNlM2RiNjkwNzUzNGI0OTg5Mzg0YmQzYzZiNTcKYjFkY2ZkMDk4NDZhZjFhYjM2NjAwNDRlNTgyZDk2Y2UwYjhjOTc3OTgyZjZmM2QzN2I1MjU3MDM2YjlmYWJkNGZkMGYwYTQ1NThmYTVlNWFjZjNhNWQzZTJkNGVlY2JiYzM2MDRiZGJiOWE0ZGIwZTYxMTgxODMxNTdjMTYwZjgKYWM1ZDVhMzQ3ZjE1YTc0NDEwNzI1ZDYyNjNhZmU2ZWFmNDhlZWYwOWQ3MDc0OTk2YWEwYTdjZWEzZWI3MDVkNTJjNGU5ZTRmNjhlMGFmNTRhODNiMzMyODk0NzJhZTU1OGU0MzJjNWMzNTE0YWY4Nzc2MzQzY2VkNjI0ZDdmOWYKYTNkMWEyN2IyYjc5ODYxNGZiOTQ5ODM5MThmY2RkYmUxZmQ3NGMwZTllNGQ3NGRlZWRlNzVlNDY0NzY0NDUxMzIyMjExMGI2NDBhMGQ4NDJiYzVkZWI5NDBlMDE0OWY5N2I3ZGFiOGQzNzRmMTk4NWIzZTk0NWQ4MjFhMDk1N2MKYTM3NmY5NWZhM2MzNTllNWVkMWU3ZjU3NzMwMDZlOGU1MTMyYjE1NjIxNmM4MTYwNzkxN2I2MGQyYTI5NGU3MTg1YjhkYmQ1M2NkYmM5MjYyNTVlOGJkYWYxZDZjMmE2MzQ3Yzg5ZjU3Y2YzZWE3MmUzZGI5YjY5MGZiZjc5OWQKMzY4Y2RkOTVjYTI2NTEyMTYwOGUwNTZmMzRlOGI1MTZhNjI4Mzc2NzVlYTc5NmZhNWU2N2E0YjlmYTZjYTZmNDFmYTdmOTFlN2JjY2Y1ZjNkOWYwM2JhMTZkMjdjMmQ1MzEyZDBiMTk3YjI5MWQ2NDUyNjRlOWQ2NTJiYTA2NWEKNmFmNjZiMmE4OTRmMzBlOGU5YzEyZGFlOTRiODk0ZThiOTJkNjdjZWY0YWRjYWI2MjY3YjNmNjdlNzIwOGQyYmFkNzM5NmY4NGRhYmJjYjYxMjc3NDI0ZjQ5ZDVhMmQ4NjlhNzQwNzQ4OTkyMDBkMTgzMTExOTI2OTFlYTQ3YTkKNTQ1M2ExZTZiNzVjYTk3ZWRjZDYxZTNjY2Y5M2VlMzZjOGUwMGQwOWQxYjRlODVhMTE3NTcxZWFhMjQ4Yzg2NTZhYjJlNTU0NjdkMDQ0ZGEzM2EzMjY5MTZiZDM2ZDI5M2NmM2FlZjIxZTkwMDE0NzE1MzhmYzNlODU3ZDUwNjEKMDE1Mzc5ZDgyOTQzNmMyZWZmMDAzZTc2OWM4NDM5MDQ0MmRhMjZkMjBkNjA4ZWFkOTJjYmViMjBhOWIyMGM0YWM5MTk5MDhjY2YyMWU3ZmJjZTUyOWQ2NTU3YTc5YmI2YmYwZDIwNDUxZGNhY2U1NGNhOTk1MzIxYzgxZDkzMDgKYjFhMjhkOWFhZGFlZjNkMTc1OWYzMTAyNGFlZTE5ZDc3OWQ3OTllZTk5NGFkNDllN2QyNzRhOTM3MmI2OTk2ZjY0YmU4Yjc1MzU4MDI3MTlmMDEzYTIyNTg2NmQ5ZDczMTExNjMyMzA5ZTgyZDkzMDU0Njg0ZGY4NGUxZjU1Y2EKNDZjODBjODhhOGIyZDhjYjMxNTk3MjMzMjkxNWI4ODZkYTk5MTRiNmNmYjJjNjFjNjNjNTBjYzBiZTMxMTc4MGIzNjZjNzRhY2Q5NWNhOGIyMWUwNTk0NWQ2NDJiMzY1M2MwYjAwNmQxOTEzOGQ3MTBjZThjODEwYzYyMjQ3NjYKMDQ4MzE2MzA5N2FmOWI5OTcxYTczZDJlMDhhOGIyY2I4NGEwMWIyMzkwNzgxNjA2MzJkODViNDRjMDc2YjJiYWU0ZDk5NTgwOThmMTczODMzNTcyZjUxY2ZmMDBmZmM0MDAyNzEwMDAwMjAyMDIwMjAwMDUwNTAxMDEwMDAwMDAKMDAwMDAwMDEwMjAwMDMwNDExMDUxMjEwMTMxNDE1MjEyMDIyMjMzMDMxMDYzMmZmZGEwMDA4MDEwMTAwMDEwNTAyYWYyZDU2OTVjYjVkMjY1MjZjZTVkN2FmNTE1OTdhZjIyYTA5OTk2YWJiZjFhZWExNzhjNzA3MzM3ZmE2ZWIKN2EwYjczZWE0MmY5ZjZiYzJkYjMzOGJhYjU1Mzg4YmM2ZTM0MWM3ZTM0ZjZmYzc5ZWRiOGYzZGFiMTY3YjU2MzQ2ZTEzMGNjZjY1YzU1OThiYzdkNzhkNjdlOWU2MmRlYjQyOWQ5ZGNkY2FiZWU3YzQ0ZWI1YjQxZTBjZWE4MmYKZTVmMTY4MTkzZmU4ZWMyMzg5Y2ZmNWI4N2ZiMzliYmJmM2Q2Njc2OWI5ODJiZGIyMjkxYWE4YzFlMTk5NTBiYjFlZWE0ZDc2Mzg5YzJlNmZhNGNmMGRiMWZhOWNlOTM5MGI3Y2NlNDAxZDBlZDE3ZTRmMTY5YjcxZjAwYzFlMGMKMzYzOThjNmViMWM3YzNlYzFlMTczYzY1ZTE2ZTZmZjRlNTM3NWFhZmE2YzE3NzliMTVhMjRlMjEzZjEwZmE3MzI5MTZkMTk0YTY4YjlkYTcwZjlkZTkzMzkxYzNhZmU5Y2Y3ZDU2Y2ExYTViODhhZDJjYTFlOTk1NjQwMzM4ZjQKZTk0N2QyZGZjZmY0MTg3MThjZGNlMDczZmQ0NjJmZTljZmIzZWU4Y2MwMGJlZDE2YjUxODRhZDcwYjg1NGJlYTg0ZjU1M2IwOWRkNjc3MTBiNGNkYTQ1ZDQ2NmQyNjhjOTljNjY1OWM0Y2RhNmU0YjJiZWUyNzcxM2I0ZGNkODkKYjEzNzE5YmVjY2RiOGI2NWY5YzEwNWI5MTY1Y2Q4ZjQ3NDk4NDM3N2RjZGY5NTRmODM1NDA4YjAxNDhkN2Y5MGFmY2JkZDY0YTMzYjJhZTRlNGIxZmI1YzM4ZDc2ODM4NmI4Y2MzYzZjZmM3NTZmNzAyNDU1Yzg5NmFlOTZlYTQKNmE3YzE4Mjg3ZjM2YzUzZDJjM2Q3MWYyN2NjNDZkNTk3MzU1NTJhMGRjZTNmZjAwZTA2MzY0MzE1YzZiODRmNGY2YzM2NmU3YzQ2NGFkYTNhNTZiMmZlZDI4MmNiMTIwZWIwZDlkNTE3MjJkODMyMGMzOTY4MGZhY2E4YzM5NTQKNDM5YjU0ZjVmNWNiNmZhZWNhY2Q4YjNhZDZkMGUzNTI2MWMxMTMwNjg2MDg4ZWU4MTZjNDZmMGRjZGMwZTU2MWIwYzJlNjdkZDJmNmIwMmQ2NGVjNzQ5ZjZjZDg4ZjVhYmNiNTNjYmYxZGVlMzIzYjBlYjljYTU1NzMzNGI0ZTQKNzUwOGMxMzhkZmU3YzE5ZDA0ZDM4OWI4NGZjZjcxMGRjMDRhY2I1YTNhNThhMmQ2MDZiMWM5NjBhYzFjYjcxOTNkZTM4YjllZjVjNjRmN2JjMDllZjU4ZjA3Mjk0ODNlZjE2Y2FmOTBlNDZkMzhlYjk0Y2I1NjM1NzRjMmNiMzcKM2UzYzM1MzUzNDYwYjI3Nzk2N2YzMWYxYmNmNjQzNTIyZTU1MmY5MDEzMGVhZTllODcxNzYzMGYxYzRmNGY4ZjNjOGEyNzkzNGNmMjY5OWU1ZDAyNzk5OGM5MWIzNTA0NmNkYzgzM2Q0NjZmOTg5OWQ1YzViZDFlNmM3OGVlNmUKNmNjZGVjMTY5NjIwNjlkNjA0MzExMWEwMGMyMjViNjA4MmY1OGI2MDMzNzM3YTllNjA5ZTY2ZTdlNDlmOTNjM2UyNzk2ODYwMzYyYzE5NTYwODk5NmE2M2Q4ZTVhYWIzZWNkZjgxZjk5YTg2YTA2MDRlYjA2YTA5YTlkNjBkYWMKNWJmNTA1OGFkM2UzYzM3M2I0MjY3NjliOWIzZTBjODFhNzU3NTllNzVjYjAzNmZlOWZlY2Q2YTBmYTA4OWYzMTZjNjExNmY5ZDgxOWE5YTNlM2I4MGNkYzMwOTk1YjE4MGZkMDIwOWE4M2U4ZDQyMjZhNzYyMjJkODYwNmRjMzMKNTM1ZTA2MTMxOWE3ZmZjNDAwMjkxMTAwMDIwMjAxMDMwMzA0MDIwMjAzMDAwMDAwMDAwMDAwMDAwMTAyMTEwMzEwMTIxMzE0MjAyMTA0MzAzMTQxMjIzMjQwNTE0MjUyNzFmZmRhMDAwODAxMDMwMTAxM2YwMWU3NWI0NTkwNTkKNTBiMjQ0ZTU4ZDhlNzEzMzRkMzY3YTU2YThmNTRlZDMyYmQ5YzU4Yjc3YzhiMTFiNjI4YjEyM2Q1Y2FiYzEwNjNmNTczM2FiYzg3NTk5MGViNjY3NTkyM2FjOTdmNDczYTdmNDQ3M2E1ZjQ0YjJhNmJkYWMzZTExNjU5NjYzM2QKNGNhZTQ0NThmNDhjMWNiZTA1ZTk1ZmY5MGEzOGUzZjA4Y2IxNWZiNDQ1ZGQ2NTk3ZDg4YjJjYjNlMjA2NTc3MzEwZjRmNGYzZGIyMjRhODkxMWZmMDA1MWM2YmI2Y2IyY2IyY2IyMjU5NjM2NDdjYjMyYmE4OGM0M2QyMmM4Y2IKN2MyYzY0OGZkZDU5NWQ5N2E1ZTk2NTkxMmQ5YmNkYzYyNDdhOTdmODhmYjcwNGU5ZDEzZjBjOTMzMWNlOWQwYzYzMWY3Yzc0YTJhOGM1MzQ2NzdlNmJiYTI0ZmYwMDI4NThjNjQ2N2JhMjNlZmJkMTMxMzJjYmJmMDQ1MTFjNWMKOTZjZTk0ZTk0ZGFjZDhjZDhjNTEzMTNmYTMyMmE2MzMxY2E5OGZiZGNhODRjNDU5NzY0NTUxMTY2MWYxMDJmNDhlNDY5OThkYTkxMjQ5OWUyYmMxMWIyNmFjZTMzOGY1NmNkY2M3MjE0YWY0Y2RmYTk4ZGZlM2FhMTMxMGIyNDEKN2Q5Y2QwZmVjZTdjN2ZkOGIxZDFmZjAwMDRlNDIxYzkxYmM3MjFiMWY5MWM0NmI0N2E1OGRkODg0YzQyMTExNmJlYzk0MTRiZTA5NjM5MmQyOGExYzE0ODU4ZDBlMjhhNDY0NWUwYjFmYjU2MjYyOTBkOWU0ZGU1YzVmYzk1YWUKZTFlNDhhZjkzN2M1ZmQ5MjVlMjg3MTZiNGEzNjMzNjMzOGQ5YzY3MThlMjk3N2Q5NjVhMjkwYzY0ZTdiNDc2ZmU0OGNmNjlkNDQ4ZWEyNDczNDhlNTkxYzkyMzkyNDZmOTE3MjM2Yzk5YzcyMzhjZDgzOGIyYmIyOGExYmQxOGYKNGEyODZiNGRjNWViNDU2OTQ2ZDI4YTM2MjM4Y2Q4NmM2NmRkMWZiZjVhNTY5NWEyMjhkYTVlOGZkZGIyZmQ5YmZlNGZmZmM0MDAyNzExMDAwMjAyMDEwMzAzMDUwMTAxMDEwMTAwMDAwMDAwMDAwMDAxMDIxMTAzMTIxMzIxMTAKMzAzMTA0MjAyMjQxNTEzMjcxNDA2MWZmZGEwMDA4MDEwMjAxMDEzZjAxOWZhNjZlNjRiZDMzMjVlOWU0MmY0ZjMzNjI0OTEyYzUyM2QyNjM3MTQ3YWM1ZjIzMGFhNWRhOWNhYmMwZGI2M2IxMmFlOTgxNWNhYzkyMzQ5YjY2ZDIKMzY0ZDkzNjRhOTJmYjI1NmZjOWNmNmE0YWQ5MjU0NTE0NGY4NDYwNTUwMjQyMTBlNWE0ZDZkZjgxNjM2ZmNiM2Y4N2E1Zjc2MzEyNjhkMjUxM2U1OTA1NTE0ODkxMWU5OTNmNDhjYWM0Y2NiMWI1NjVmNmE4NjI1NDg2OGQyMzIKYWU1ZDE4ODQ0OTEwZTFkMDg0NjQ4ZTg5NzZlODZiOGExZTJmYzFjNDk5OGQ3Y2FmYTMyODVkMjZhYjkyMWNhYjEyMzJjMzU0N2I3MTVjZjRiMzg3ZTRjOTg1YTIyYmQ5NDMyNDYwOTUzYTE3NGNkMGQyZmI1MDQzMjhhYTI3MmUKMTkyOTY5ZTBkYzFlNDM1YTM3MTFhZDBlNDc4NzY2Mzc2YmE2NDhlYTQ1NTdiZDJiMWFhMjBiODI4YWEyNDY1ZjA2NDU2YzY4Njg5NjA1NDNlMDRhY2M3ODIzNTcyNjY0ODQxNzgzMWM4ZGM0NmVhMjUyODQ4NWEwZjgxYTUwYTAKMzhkNzRjNWZkMTNmZTg1NWQxOTQ2NWYyMzRjNzA2Mzg0ODc5NWIzZmQzODM1MTFmZjQ5MmZmMDBkMjhhMTcwNTk2NzIyYjM5MzQxYjZjZDJmZWNkMjcyNmE2NmIzMjczZTA4YjY4NTI0NzA1OTY1OTdkNzE3OTJiZDg5OGJkOTYKN2MwNzVmYTNhMThkNTNlOTVlY2EzNDlhMTlhMjQ0Nzg2MjhkOWI0Y2RiMzQyMzRjNGY4MWFhM2ZhNWMwNTRmYzE1ZDFmNWExYTM0OGUyZmEyMTBhMzYyMWFkNDZjYzRkOThmZTliMzFmZDM2NjFmYTZkNDNmNGQ5ODFiNThjZGIKYzY4YmM2OGRlODhmMzliYTlmZDhhNjZiMmQ3NjEzMmNkNDZhMmM2OTFhMGFhZWI0NTFjMGViYWVhNjg1OTE5Yjg2YjE2NDhmZDhlNGJlYmRhOTllN2FkOTY3OTM0OTVkYWIzNTE2NTdiYWZkYjdkMjkwZTNkYWExYWZmODFjN2IKMTQ3ZmZmYzQwMDM3MTAwMDAxMDIwMzA0MDgwMzA1MDkwMTAwMDAwMDAwMDAwMDAxMDAwMjAzMTEyMTEwMjIzMTMyMDQxMjEzMjAzMDQxNTE3MTA1NjE4MTIzMzM5MTkyYTEzNDQwNDQ1MDYyNzI4MmIxZDFlMWZmZGEwMDA4MDEKMDEwMDA2M2YwMmNhNjY1NjUyYjAzNjYyOGRlNDZhYjE1MTI0Nzg1NDU1NzZiMTU3NGVhOGI1YmYzNTk5MTY0NTkxNjQ1OTAyYzgxN2JiMGFlYjY0OGI5ODJhNzg1MjA3MTNiYmY0ZGM5Yjk2Nzk5ZjI0NzYwYzk3NzQxZTczNzMKZTJiNjFmNGFlZTMwMjFlNzVkYzczN2E4NGU2YmFjZDQ3MWY2NzEzODg1NDRmMmE2ZTM5ZGQwMjk2ZWVkMDBiMjYzMTA5YjMzN2RiNDNjMzI5ZDEwNzMzMzU3YTk2ODc3NTMzZGUyZDRlODY2YzEzMzcxZjQyODExYzIyMmQ5OGMKMTAwNTM0NzQ2ZWZlZDk4MmQwYzcxYmNkYTcwODM2Y2FhZDkzNTQyNmNiOWE5NzVhZWU2MzZiOWE1Mzk5NjM1ZDNiYTY4NTA3MDNjMDlhNzAxYzk1NGFkNTYyOTljNTZiNzI2ODlhMjNhNTM3MDhhYTJmNzJmNjUwOGE5NDc2NmEKMjliNDRkNjUyYThkMjgwYWI4MmE0MzQzMDAxNWVjNmRkNmQ2YTVhZTc2Y2NkNTU2ODJkNzNiY2Q0ZjY2NTY0MmIyMTU4YWFhYWI2NmE5MGZlOGE5MGRjN2IyOTFkMGRjYWIwNjRiMDU0MGFhYzBiMmE5MmFhY2NhODZjOWI1ZjUKMWM5NTU2NDA1NjEyNTc2MjdjNTZhOGE5NmE5M2RiM2RjYzU2MzY2MmIzMjEyMjdkMTQ4Y2ZlM2I5NTU1NjRjNzUwYmE4NWZlZDBhZWFhOGQzZWFhZTQzNzkxZDk0ZTI2ODJmMWU2ZGEyZDY5MTFlNGYwYjVjY2JkMTNhMmM4YzgKZDJkYTFiM2M5NTU3NTU3MTg0YTk5NjQ4MjI0MTUyN2M2ZDRlZWQyYmVkN2YwNjE1ZWZlMjFlZDA4YWZjNDNiYjQzNTc3NDVkMzFkZmMxNTNjMzc0YjNlOGE3MGZjMGUyY2ZkMDJiYmUwNmZmMDA5ZDRhMTc4MWZhYjljYTdhNGIKMjE0M2ZkMmNhYWJiYWU3ZjdiY2JiZmI1NTkyYWYwMGE5YzU4YTE4Y2VmNTI4MzU4NWEwMjAxOWE0ZWNjMDQzNjkxMjZlZWExNzIyYjkyZTRiOTViOGFmZjAwOGFlYzM3Mzk1YzgyMDc3NTM4OGRkNzZmNDA2NGFmMzVkMGZiODUKNDcwM2MxOTg1MjU5OGFjNGFhMmE5OWFhZDE1MzcyZWFlNGFiNmNjMmJiMTBmYWFiYzI2YWI0NTM2Yjk1ZWM3ODc0NTdhZGMzODM3NWRmNzBhZmU0OWZmZmM0MDAyODEwMDAwMzAwMDEwMzAzMDQwMjAzMDEwMTAwMDAwMDAwMDAKMDAwMTExMjEzMTQxNjExMDUxNzEyMDgxOTFhMWIxYzFkMWUxZjAzMGYxZmZkYTAwMDgwMTAxMDAwMTNmMjEzNTBiNjdmMTE5NDZhZjYyOWU1ODljYjBkMDgxZjRhY2M5MTQ4NDBjZDAzNjM2M2Y1MzU0OWEzN2I5ZWQwNzQyZDkKZTBjNzU5YmFjNmQzYzFhMTc1YWRmOGU4YTJiNDdjMWQ5MGUzMGRiYjNlMGZmYzAxY2MzNWJmODhmZDA0Mzc2NmU5YjBkOGRmYWY0ZTBlMDZmODI4NWQyYjNmMjIzMDJkMTIyYmQyOGJiMjJlNDc5YThhZGIyMTE5YzM2NTZlY2EKNzBiZDk4ZDhmYTMxYjJmYTliOGE5N2FiNDUxOGFmNGU5M2NhMTU5Y2NmZDA2ZjE4NTcyYzMyMzQ3MmU1YzJmODYyMTZkMzI5NDZjNmZkNzc0NWEyNzUzMDlhMmE2MzA3ODEwNWVlNjIyNmQ4ZTg0ZWExZjZiMDg2ZTY2NThjYTkKZmEwMDg0YmQ2ZmE2YWY4MTk4MzZkY2MyOWMyZDA5Nzk0ZThmODMyMjVmYWNiNGZiMThhMjQyZThmNTJiODMwNDMxZTBjYzMxNzgzMDFhNTVhOGJmZjFlNTZjMTVhYTE2YmMxOWE3MDM0YjBjYjJhY2FhZjk2MjY1MjEzYTJkMGMKNGI2OWFmNDliMjc1NmE4ZWVlNzg0YzVlOWE1Mjk2YTM5MjhjMmRhMGIyOWI3NTNiMjQ2OGI5NzljOWY4ZmQwYmJjNGY3MjVhZjQ5NWQxODhiMDRkNTZhMWNhNThiODFiMzVmMDQxMjY1YTY4ZTUzOTQ4ZWU0MWNjNWI3MWEyNTUKODg0YzE4MDYzMDFkMTJkODdmNzE3YmFlZTMyMzU5NDFiOTlmZDA1ODRmMDMyYWEzYTQ1MGQ2MTc4ZDg0NzcxNWViMDgxMzY4NWI5OGJmNzNmOGNjNmExOTkxNGQ5YjE5NzUzNWRkOTRlZWU0NTE2YTg0ZWI0NGIwYWI3YjEyZWMKNzkzNzljOGFmMmRhYjUyMzc2ZjY4ZDIwNzExMjQzNWJkMWRlNjRhNDU1ZTRkNjA5MDliMjBmODE4YTMxMGQwMWY2MDhmN2MyMjE3YmM5MzQ4NmJmOTY3MDUwODc0MWIxNmJmMTg2ZjU5MjkxODY1ZDE5MTk1MDI4YjJiMWU5YmEKYzg2MWIwNzQwYjNjYTFiZjk0YmI5YTdiNzhiMWNkYWUxMDVhYzQxYjZkNzc2MjVjMTViYTM0YTg2MzY4NjhhM2E0MTE0NTVlNDY3N2U4NGRiZTQ1ODAyYTFhY2Q3MjMxN2Y2MjdkYThlMTQ2OGVjZjcxZTk3ZWFlZjIzY2FiMTYKYTI2OWU0YjVjYTljZGIwMDdhMDk3ODM1OWY5MzUxMzViMjdlYzVlMTY5OTIxZTQwMzcwYTQ5NzIxN2JhMjkwNjVjYTk0ZDZiNTBlNzRkZWM3NDE0Y2QwNjJmZDQzNWVhOWU0ZDI5YTBjY2Q0MmQ3MDM2YWYyMzU2Y2NiZTI1YzAKZTMyY2I2ZmQwNDIzYWZmOGY4MTI0YzI3YTdiYzA5OWY0MjZiZjczMjZhMmE1YjkwYWJhZGJlZWQzZjgyNmMyZTAyNWYyNjUwN2VlMzdjOTZkYjM1ZWZmOTAwYWM0ZDViMWMxZDA5MmQ4ODdhYTM4YjE4NTM4N2JhMWE3NzFlZTYKYTlmZDE4NTkxODE1NThlODkzMTcxYjNhMjU2YjEyNTc3MjgyNGNhZDM2Y2U2MWExYWY4YTMzZGQ1ZWM3Zjk0MTJmYjdkMWUxZjkyYWFkN2U0ZTI3YjUxODVmMTRhMWY3ZDIxZTRjMWU5NTI3MWZlYjIyZWY4ZjYyNzk1NmNjNTcKYjkyOWNhNDA5Y2IwZjI4YmQ0YzhmNTg3ZDA0NzE5MTE0NTNmMjI5MzM0ZTQ4MGQ4YzZjMWM5YzgwZDQ3NDRlMjg2MDc3OGQwZjQxM2U0Njk5NjgxMjRkYTU3YjhkYjZhMzYyMTFmNzU4MzYzMWRiMjMxNjlmMDNiODFjOGM5OGEKMzI2ODUyMmI3OTRmMjc3ODRiYjE5MTI4OWIxZGE0NDE5MGM1ZjVjZDEyMWE3MzIzNzY2NDQ4MjkyMGNmYTBkMmU4MzQ3YTFjODI3ZGNlMDBkYzIzZDZlNDkwNGM3OTM0MjkyMDRmYTNhMTUxMzdiOTI4ZDIyM2MxY2I4ODc4YzMKYTM3NWEzMWE4N2NhMTQxZjUwN2ViMmI3NDZiYTMwOTQ4NWQ0ODU5MjA0MGMyZDBiZTlkODg0MzFhOGMyMTg3OGU0OGZmZmRhMDAwYzAzMDEwMDAyMDAwMzAwMDAwMDEwNmIwNjMxYThmNmU0NGUwYzc2Y2ZjNmExOTdjMzRiOWMKZDVhNWUwZmI1NDAzODQxMmRhYmY0ZDIwMGRhOGViY2Y2NmVhMjY0MjA3MGUyZWNiOWU2ZWYzZGFhZjQ2YzY0MTg0YjcwNDkwMGUzMmZkNGVkMTMzY2Q1ZDZkODBiODEyNDEyY2JlODA5YWUxOThlNzcxMDA5NDkyMzNmZjAwMmUKMjBkZGIyMzM5OGM5YjA5MTdjYjVkYTRmZTE4MmJkNmI5NGYxZDVhYjEwMDM1MTI4N2M3OTAyZTViNjgxMzkxNDRkZDhkYzNlM2RmYmM3ZTgxOTlkMDA5OGNkODE5ZmNkOWNkZDQ4YWIyMjQ5MjgwZTc4MmZjMThmZjk1MjdhOTYKMjlhNDI5MjIxMGY0NjVjYzUyNzE1YWJhMGVhMzljNmViZmZmYzQwMDI3MTEwMTAxMDEwMDAyMDIwMjAxMDMwNDAzMDAwMDAwMDAwMDAxMDAxMTIxMzExMDQxNTE2MTIwNzE5MWYwMzA4MWIxZDFjMWUxZjFmZmRhMDAwODAxMDMKMDEwMTNmMTA0Mzg5Y2MyMWM5NjZiYjhiNmRkYWNkYmIzMGU2ZTE3NjAzMTc2ODIwODNmMjI2YmQyMjBjMzgyM2FhNGVhZDM4YjAzZmEwZmYwMDlmMDgzYTA5ZmFiYzFmYTM3ZDY1YTcwOGJkYzM3NDE4OTg2N2JiMzk4MjBmY2IKMjFjZmUyYzY3MmU1OTAwZmQ1ZjEzOTY2ZjIxMzk3OTBiZDhmZGNlMDJlMTg0NDQxZTM3ZjAwM2UwMzU5ZTAxNzAyZTRjZTljOGU2NmQwZmFlM2M1Y2Q5M2JlZTU0OGMyNjZhZmE3ZmNkYTMxODIwYmE5NjdmMDAyMTE2YjFjYWUKNWM0MDFlMjgxOTBkNzVmMDUzNjBkZjY4MmNlNWJkZDI1MDRmNzFlMGIyY2NkZjBkODg0N2NjMDNhNDcyZTY3NTdiOWIxYzFmYTdlZmZmMDA1MGIyNTk2ZGI5OTc0Y2Q2MmU0YjliNzRkYzY1Mjk0YjJkYjZjMzBjYTFiMmNiZTQKNGMwYjcxMTJjOTZkODllMzcwOGY2NGU3OTY1M2VlNTJjYjJjYjZkYjEwN2NjMTFkYjE2Njk5MmFmZWNmZjNmNzk1NmExYmFiZWJiMWVjOTMzNmY2YWRmMjU3MWNmNGNhNjY1OTk2ZDgwNmI2YzY5M2UyMzE2ZjgxZTI3YjJlMjcKZGYzZmJjNDYxMGMyOGM0NjI1YzM4MmY2MjY3OGNmY2U0YzA4NjMyZDkxYzc4MTRlZWQ1MTZiYTViMDg3ODhkNjc5ZTBlNjY3Y2M0NjQwN2QyYzNkNmVjMjQ2OTA1YzZjYjRkNTg0NzUyMWY1NjkwYzA1MDdjYzA3YjljODE2NjMKYTEwY2IwNjI0YjNhZjAyOTRhMjI0NTc1ZTM3NzczYmVlNzllNDYzZDQ4NmE0MWY1N2MzOWY4ZWVkOTJiNjRjYjJkYjZjYmU0NjEyM2VkZTUwNTMyMTFkNDJlOThlODJkYWM2NjRiOTM4YmI4NjViYjBlNTIyYTc3MTIzNmRmZTMKZmVhZDNmZjFmZjAwNTdmMmU3YzFhYjU3NjhjZTdhZjFiMTZjMzBhZmI0NTNlNDdhYzFhY2QzNjc3NzM5OWQzODI1M2Q0YmZhOTVmNTM2YzI5NmRlYWY4NGMyNzY0N2NkYjIxYzQyZWNmMDM2MTg2MzU2YmMwYmIyYjk0ZTQ4OTEKZWJjMDY5N2RhYzMyNTliNjg4NWUwMGY4NmFjYmRkYWRmNTZkOTAxMDllZTU5NGIyY2NjY2M5MjU5OTZkYmUwYjNjMDJkNDFmMzYyMGI4NDA2MDc4MmNhNmQ5OTk2NjdjNjU5ZTA2MjEzNGYyMzExMTBjMmI2NTk5OTk2NmRmZTgKMGMzMGRiZTA2MTg2ZGJmZmM0MDAyNTExMDEwMTAxMDAwMjAzMDAwMTA0MDIwMzAwMDAwMDAwMDAwMTAwMTExMDMxMjE0MTUxYTEyMDYxODE5MWIxZDFjMWUxZjFmZmRhMDAwODAxMDIwMTAxM2YxMGVlZGUwOTBmODZjZGUxODgKZjUyMjk5M2RjYzhlNjkyYmI5YzE2ZGYxMmNmMTk2NTkyNzBmZTMzYzg3NzE1ZTE3ODQwOWU2ZGFmODFmZjVjMDZiYjYwMzZkMDNkMzZiZWNmYjZjNzQ2MGZiYWM1ODcwZjE5NjU5MjQ5MjcwNTgyNzRjNzA5YjhmZGZmODhjNjEKMWY2YmE2NjRlOGQ1ODU5OWU3MmNiMmM5MjQ5YjRmM2ZhMjBlMDVmZDk5ZmRmZjAwZDQyMTEwY2YwODAwOWM1ZTNmZDkxYjJkYjc4MGIyY2IyYzkyNjFmMWMyNmNjZTA4ZTExODg5N2NmMDFjMzcyN2U0NWUyZDljZjMyYTFlOWIKNjIwODIwYjJjYjJjOTI3NDg0MmU5MjQ2M2MzMGJmODZlMTQ0MTI1YTMxZWFjMTBlMmViYmIyMGZiMTEwNDE2NzE5NjQ5Njk3NzJlZDdhNDViODk2NDJmZGUxODM5MGU5NzkwZjAwYmNlOWQzMDcwMTA3MTk2NTkyNWRlYzJkM2QKNDY1YWNhMjZjNDBmZTUyNjFiMmUxNWEwZGM5ZjAwYjA5ODIzNzI1MmM2MjE4NjM4YzI1NTg0Y2IxYjEzM2JiMTE2YzNhN2RiNjc5NGRmZjNlNjFhY2I2Nzg5MzEyYmU3YjAwNzk2Y2JkZDlmNmYxYzA5ZTU4NmU5ZDQxZWFjNWIKM2Q2NWUxZTUxMTUzNDc4Njc4MzNhM2Y2OWNlZTVmMGNhZjU3NWFjY2ZkOTZiYWRiY2JiODJkNTlkM2IwZTZjMWMzZjkwZmExMmY5OTE0YTYwZGQxYmM5MzVlYWQxZWVjZWRmYjI2YjVmNmJjMDI2ZGYwYzJiODJhMTk2OTg1ODUKOTBlNTkxOWE1OTExMjEzZGU5ODg4ODA3NmNhYmI2MDNhOGI1OTlmZDVlNGUxMzYxOTM5MzFkM2FiYzcwMjYxNDNmNDZjOTFhOTNjMTk3YWYwMWY2ZmYwMDhmZjcxZWU3ZjI1ODc2ZmU0YjA3YjNmYmJjMzhmZTU2NWVjZTUzODQKODk1Zjc3ODk4MmNmYzVlYTU4YzJmNThiY2FjMWUwYmFjZjUxZjY4ZmIyMmQ4YzNmZjc1ZmJiZjk4MmY3ZjliNTZhZmU2M2FmOTI3ZDMwZmE5ZWM0M2Y0ZGZiYWQyNzFlOTk5NzI1ZDhiMzIwZDljZWUxNzYzNmE3ZWUwYmRjZmYKMDAzNmJkNGJlZGM2YzZhNzFkZjA1ZTljOWQ0NmY2MjNlYTJlNGMxNmM4NDQ3OGViODUyZDhlYzZjNDJhOWRhN2UyNTFkZjFiZmEzMjZjZTA2NDJmNzYyNzE2NDViYzFlMjM1Yzc4YjNlNDNmNjI2OGNiNGE0ODljNmZlODc4Y2UKMDUyNjcwMzFjOGYxYmNlYjBjMzYwYzEyNzFiNmYxOTA0NGZmYzQwMDI2MTAwMTAwMDIwMjAxMDMwNTAwMDIwMzAwMDAwMDAwMDAwMDAxMDAxMTIxMzE0MTUxNjE3MTEwODE5MWExYjFjMWUxZDFmMGYxZmZkYTAwMDgwMTAxMDAKMDEzZjEwNmE3MzA4MWQ2NTIwNmQ3NTRmOWU1MjhmNmEzMWFhOGEyNDE3ZDI1MDQ5ZWYwYzU0MTUwY2Y3NjBkOWI4ZDNkYTY5ZjEyZmY0OTdhMTA4MzA2MWIxZGM0ZWEyYTFiYWJkYzdkNjBlMWU1Zjc4Yjk2OTk1NWJiOGMxN2MKYWNiMmI5MWYxMzRmZDIxZTcxMDAxNmZiMjAwYTNlMzg1MzgzZjY5YjNmODUxN2I1M2Y2NDdiMGExZTg0YjBiOGIwNTk5MDFhOWIwYjJlODE3ZWEwYmVhNDIwZjU4YTRjMzgyMzQ4NmRmYzhmN2I2YjY1NjExYzE2ZTI4MmFkNDEKMTk5NDA3NzZhM2ZiMjVhMzA2MjE4ZThhNzk1NTQxMWE3YmExYTA2Y2E2ZDdmMDRkMThkNGUwODUyYWM2ODhiZDA3ZDQ0M2QyZTdhMDgwNzVhYzBmMmQxZjhjYTJjZDQ2ZDQ1Y2IyMGViMGVhMmNmOGM2N2Y4OTYyNzkxN2JlYmUKYWEwYjE4NzA0MjU4NWQyMTg2YTVkYzJjMDVlNjUyYzI4OTcxYzE1MmUzODU4NjExMTM2NDY1ZjQxOTIxOTk5OTk4MzJlMGMyYjlhYzRjN2E0OWNlYWJmYmI5NTBiOGZiNjI4NzQ5NDYxNzQ3OTk2M2YyNTc0ODAwZjY4YWM5NTQKMDk3ODNhNjA1ZjAzOTA3MTEyMzk5NDI5YTg1YjEyNTY1YTYxNWU2Y2U2MzBjMzZmNDFhOTVhOGIyZTVjMTk0YTNhNmZkNDE0MDAwZjU1YjliYjBiMTk4MTYxM2FjYzY1ZGIyOWRlODZmNjdmODNlZmQxNmM0YTQ4MjIwOTUwZjUKYTU1NDA5OWI1OTcyNDU1MzcxMjk4YzE3ODFlMTgwMDQwMzY3MzBiYzFiZjU1OTcyZTVjYjk0NDNhN2RlNTYwOTg4NDFjYzVkNzk5MTFlOTFmNGU2YWNjNWFhMDNmOTBmZTdhNTRhODQ2MTE4MmQ4OWI2NWNlODZjODhhYzMwYzgKOTYyNzEwYWM0MmE1NzI5YzNlODY5MmUyZTIyY2JmNTFhMGIwY2NkOTU0MmNhZGMwYzQwMzZjY2E0MmE0NGE3YzRiY2Q2YzFiN2VhZTU0NjBjZjZiODFmMTZmNzljMjhjMTg4NzBkNTQ2YmQyM2FjZGNjYjEzMzA3YzU5YWI5NDIKNDJkZjE4Y2IyZjE4MjBkZTI5ZTYxODYxMjIzMDNkYzA5YzY1YmMyM2Q2OTQ3MTgzMjgyYjJmNDUxMDg0ZTJhNWY4YTlmNzZlMmIzNWQ2YWUzOGI0ZDVlMDRhZmNlNjU1YjYxYWU1MTNmMGZkOTY3ZTU1NzVkNDUzZjYzMTUxNmYKYTA2ZTJiYThlNDA1ZTI5OGU4MzRiZTYwNzU4ZDExYjg1MzEwZTM3MTBiMmExMDZkNDhhOGI1Y2U1MDkxMDE3ODIwODUwYmQ1ODRhZjM3YTAzODI1MGM5ZTU5NTk2ZjcyNmVhNjhmOGNlNjZkZDY1NjA1OGE3NDk3YjM2ZDQwNmUKMWE0NWE5NTcyODIyNmJkNzIxY2MxYzQxNWQ4YTU4MjUwNTkzMDJiMDIyZTM3NjFkMDM4N2JjNTJlMjU5NGFiNThiNTE1MzFhOTUzMGY3OGQ0YTA4MjMyYzE1ZmI5MTEwMTc0NGIyMThiNzg4NTQwZjdhMTgxZTY4ZjUwZjk2MWIKYmY2NjI2MjFmYWQxNjJiYjgyNzU0NGM1YWU1MjFmMWExYTVlYTA5MmQ0YTRlYTQ3MTQzMWQyNTllOWY4MGNjNDE1MTcyZDZhMTZlNTAxYTU5NzNmM2E5OTJiNzgwNGI0NTQ2NTcwYTJlMmNmZDkwY2UxZTRlMmM0YmY4YjlhOTcKOGFhZGY3ODVkZDZiYjEyOTg1ZjQ1ZTIxNTM1ZjJjM2MwOGYwYzc3NThmOTRlMmJmMTJlZDUwZWUxMzFjNTVkODk3ZTMwOGJiMWZkNGI2ZmNmNjU1MjgxNzZiZGUwN2FmOWE1ZTUwMWVkMGNlMDY4ZTEyMmNlZWQ5ZjgzNjc5ZDQKNmIwNDg0MDk0ZWNkZTYyMTUzMDBhZDQxZDdmYWM0YzkzM2I2NzgxYjhhNDJmMjE3ZTA0NjI1OGFhY2RiZTQ5NmUyMGJiMWI2NzJkMzg4ZTYyMDU0MDAxY2RhOTVkZTUxNTExNTU4ZTM3NDg2NjVjYThjZjQ1ODE5NjljZWQ0ZjYKOTY1OTBmNzhhNmQ1NzkyZTNmYzExOTQ2OGFmYTg5YjlkOGI2NDhiYjY3NGRhMDBkOTVlMGI4YWI4NjkxODhmNGJkNDBmZGY2ODAwM2U2MWQzNDYwYTJiNTlhOGFkMThjYTZmZDkzMGNjYmRjN2U4OGE2M2M5NWZjY2I3NGJjMGIKZjYxNTY0NzVjMmNkNDg5OTFiMTc1YjkwZWRiYWRiNzFlMDA4MTU4MWMxNTc0Mjc5NDEwOTM0MWIwNWU1ZDA3YjBjYmU5N2I1ZmYwMGI1OTVlZDUzNzIyZTA5Nzk4YmM2M2YwMDkwYjE4MGY2MjUwZDA4ZTgyY2IwNjQzY2U2NzAKZDE4Y2I3YzI5Y2MyM2IxMmI2NTdkZTM5NzU4ZTM0NGM4MTAzYTA3MWU2MTc3NWExZmYwMGQ1OTlhNDQwMWRkMzZlNGVkMDcwMDk1ZjExMzM1MGYxMjc1YmY5MmE5MTU2YmZlNzI4YjFlZjdmYzQxZjQ3YjdmYTRjYTAxZWY3ZmUKMTAwY2ZlMTJmZjAwNmRhNzVmODg5NzBiNGVhMmZjODUyYTRlODhmYjg2YTc0NjZkNjIyYmU1MjFiZTVhYjk4NTIzYWM3ZjBiMjE2NDhlZDRlNTJhNTkxY2QyZDgwNWY5OTY0Y2E4ZTBiODNlNTAzZDc5OTY2ZjUwZGMwYmQyNmYKYTgzZDFhOTY4ODVlYzZmODk3OGI4YjE2OTEyMjQ4ZDZiMTEyMmFiYzQzNGRmZDA5NWE0OThlOGM1NGVlODY0YTNiZDQ1N2IxMTZmMDllZDJmYjIxYzU0YWIxNjgzNTBmZjEyYjI3Y2ViZWE1M2Q4MTA1MTA0Mzk3NTMwMGJiNTEKOTk5MzQ1MDkyYzg0ZjI4MzMwYzc1ODc2NDM1ZWI4MjYwYmMwMWU2MDVjMmUxODMwNDRiOGE2MDQ1OGM1NzJlNjNjMTNkYTM1M2RjNDAzMDk3YzRhNzYxMDc4ODNjMTAyMWEyMmU2NjExNzkwOTkyNzUzYzg5NmU0NGNmNWFiYTkKMWZhMjE2MDk0OTA0MjM2ZjMxODQ2ZTYyYmE4NjU3MDRlNDg5YzkxMGI1MTI5OWM0YWY1MzQ4NDU4YzQ0OTg4NWYyNDY1MDViNDI1MWZjODgxYjA5MDczODc3ODIxMGM0MTA3MGQ0NGJjNDVlNjM4NjQ4M2Q2NzcyNjMxNTFjMTYKNDFlZDBlYjEwYzM2NDY2YTBlZDEyNTkxNjZhMDEzMDk0ZDg4YTZjODNlMjEwYzRiMmM2ZTEyZTY3MDQwOTkyNTg1ODI2NDYyYTNhYzVhODRiODk1MjdmZmQ5fX19fX0KClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjVccnRsY2ggXGx0cmNoXGxvY2gKClxwYXIgXHBhcmRccGxhaW4gXHM1MVxzbDI4OFxzbG11bHQxXHFsXG5vd2lkY3RscGFyXGh5cGhwYXIwXHNiMFxzYTE0MFxsdHJwYXJcY2YxN1xkYmNoXGFmOVxsYW5nZmUyMDUyXGRiY2hcYWYxM1xhZnMyNFxhbGFuZzEwODFcbG9jaFxmM1xmczI0XGxhbmcxMDMzXHFqXHdpZGN0bHBhclxzYjBcc2EyMjV7XHNjYXBzMFxjYXBzMFxjZjFcZXhwbmQwXGV4cG5kdHcwXGkwXGIwXGRiY2hcYWYxMlxydGxjaCBcbHRyY2hcbG9jaFxmczIxXGxvY2hcZjhcaGljaFxhZjgKTnVuYyBhYyBmYXVjaWJ1cyBvZGlvLiBWZXN0aWJ1bHVtIG5lcXVlIG1hc3NhLCBzY2VsZXJpc3F1ZSBzaXQgYW1ldCBsaWd1bGEgZXUsIGNvbmd1ZSBtb2xlc3RpZSBtaS4gUHJhZXNlbnQgdXQgdmFyaXVzIHNlbS4gTnVsbGFtIGF0IHBvcnR0aXRvciBhcmN1LCBuZWMgbGFjaW5pYSBuaXNpLiBVdCBhYyBkb2xvciB2aXRhZSBvZGlvIGludGVyZHVtIGNvbmRpbWVudHVtLiBWaXZhbXVzIGRhcGlidXMgc29kYWxlcyBleCwgdml0YWUgbWFsZXN1YWRhIGlwc3VtIGN1cnN1cyBjb252YWxsaXMuIE1hZWNlbmFzIHNlZCBlZ2VzdGFzIG51bGxhLCBhYyBjb25kaW1lbnR1bSBvcmNpLiBNYXVyaXMgZGlhbSBmZWxpcywgdnVscHV0YXRlIGFjIHN1c2NpcGl0IGV0LCBpYWN1bGlzIG5vbiBlc3QuIEN1cmFiaXR1ciBzZW1wZXIgYXJjdSBhYyBsaWd1bGEgc2VtcGVyLCBuZWMgbHVjdHVzIG5pc2wgYmxhbmRpdC4gSW50ZWdlciBsYWNpbmlhIGFudGUgYWMgbGliZXJvIGxvYm9ydGlzIGltcGVyZGlldC4gTnVsbGFtIG1vbGxpcyBjb252YWxsaXMgaXBzdW0sIGFjIGFjY3Vtc2FuIG51bmMgdmVoaWN1bGEgdml0YWUuIH0KXHBhciB9" +} diff --git a/frontend/src/lib/fixtures/r4/resources/diagnosticReport/example1.json b/frontend/src/lib/fixtures/r4/resources/diagnosticReport/example1.json index 182ada1d..e314f13c 100644 --- a/frontend/src/lib/fixtures/r4/resources/diagnosticReport/example1.json +++ b/frontend/src/lib/fixtures/r4/resources/diagnosticReport/example1.json @@ -73,19 +73,21 @@ } ], "status": "final", - "category": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "252275004", - "display": "Haematology test" - }, - { - "system": "http://hl7.org/fhir/v2/0074", - "code": "HM" - } - ] - }, + "category": [ + { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "252275004", + "display": "Haematology test" + }, + { + "system": "http://hl7.org/fhir/v2/0074", + "code": "HM" + } + ] + } + ], "code": { "coding": [ { @@ -126,4 +128,4 @@ } ], "conclusion": "Core lab" -} \ No newline at end of file +} diff --git a/frontend/src/lib/models/datatypes/codable-concept-model.ts b/frontend/src/lib/models/datatypes/codable-concept-model.ts index 1bef1d5d..5439ce5b 100644 --- a/frontend/src/lib/models/datatypes/codable-concept-model.ts +++ b/frontend/src/lib/models/datatypes/codable-concept-model.ts @@ -16,7 +16,7 @@ export function hasValue(data:any ): boolean { export class CodableConceptModel { text?: string - coding: CodingModel[] + coding?: CodingModel[] constructor(fhirData: any) { this.text = _.get(fhirData, 'text', ''); this.coding = _.get(fhirData, 'coding', []); diff --git a/frontend/src/lib/models/datatypes/human-name-model.ts b/frontend/src/lib/models/datatypes/human-name-model.ts index 450e7de9..7b0e587a 100644 --- a/frontend/src/lib/models/datatypes/human-name-model.ts +++ b/frontend/src/lib/models/datatypes/human-name-model.ts @@ -12,7 +12,7 @@ export class HumanNameModel { constructor(fhirData: any) { this.givenName = _.get(fhirData, 'given', []).join(', '); this.familyName = _.flatten(Array(_.get(fhirData, 'family', ''))).join(', '); - this.suffix = _.get(fhirData, 'suffix', []).join(' '); + this.suffix = _.get(fhirData, 'suffix', []).join(', '); this.textName = _.get(fhirData, 'text'); this.use = _.get(fhirData, 'use'); this.displayName = this.textName ? this.textName : `${this.givenName} ${this.familyName}`.trim(); diff --git a/frontend/src/lib/models/factory.ts b/frontend/src/lib/models/factory.ts index 3164ddd4..f7377d9e 100644 --- a/frontend/src/lib/models/factory.ts +++ b/frontend/src/lib/models/factory.ts @@ -31,7 +31,12 @@ import {ExplanationOfBenefitModel} from './resources/explanation-of-benefit-mode // import {BinaryModel} from './resources/binary-model'; -export function fhirModelFactory(modelResourceType: ResourceType, fhirResourceWrapper: any, fhirVersion: fhirVersions = fhirVersions.R4, fastenOptions?: FastenOptions): FastenDisplayModel { +export function fhirModelFactory( + modelResourceType: ResourceType, + fhirResourceWrapper: any, //this is a ResourceFhir object (database structure) + fhirVersion: fhirVersions = fhirVersions.R4, + fastenOptions?: FastenOptions +): FastenDisplayModel { let resourceModel: FastenDisplayModel switch (modelResourceType) { diff --git a/frontend/src/lib/models/resources/diagnostic-report-model.spec.ts b/frontend/src/lib/models/resources/diagnostic-report-model.spec.ts index 93c90360..551ca82b 100644 --- a/frontend/src/lib/models/resources/diagnostic-report-model.spec.ts +++ b/frontend/src/lib/models/resources/diagnostic-report-model.spec.ts @@ -18,8 +18,19 @@ describe('DiagnosticReportModel', () => { expected.status = 'final' // expected.effectiveDateTime: string | undefined expected.category_coding = [ - { system: 'http://snomed.info/sct', code: '252275004', display: 'Haematology test' }, - { system: 'http://hl7.org/fhir/v2/0074', code: 'HM' } + { + coding: [ + { + "system": "http://snomed.info/sct", + "code": "252275004", + "display": "Haematology test" + }, + { + "system": "http://hl7.org/fhir/v2/0074", + "code": "HM" + } + ] + } ] expected.code_coding = [ { system: 'http://loinc.org', code: '58410-2', display: 'Complete blood count (hemogram) panel - Blood by Automated count' } diff --git a/frontend/src/lib/models/resources/diagnostic-report-model.ts b/frontend/src/lib/models/resources/diagnostic-report-model.ts index 0c81e919..64422b38 100644 --- a/frontend/src/lib/models/resources/diagnostic-report-model.ts +++ b/frontend/src/lib/models/resources/diagnostic-report-model.ts @@ -65,7 +65,7 @@ export class DiagnosticReportModel extends FastenDisplayModel { this.performer = _.get(fhirResource, 'performer.0'); } this.has_performer = !!this.performer; - this.category_coding = _.get(fhirResource, 'category.coding'); + this.category_coding = _.get(fhirResource, 'category'); this.has_category_coding = Array.isArray(this.category_coding); this.presented_form = _.get(fhirResource, 'presentedForm', []).map((attachment: any) => { diff --git a/frontend/src/lib/models/resources/encounter-model.spec.ts b/frontend/src/lib/models/resources/encounter-model.spec.ts index 9fe2f800..2f4ee2ab 100644 --- a/frontend/src/lib/models/resources/encounter-model.spec.ts +++ b/frontend/src/lib/models/resources/encounter-model.spec.ts @@ -27,8 +27,8 @@ describe('EncounterModel', () => { it('should parse example2.json', () => { let expected = new EncounterModel({}) - expected.period_end = '2015-01-17T16:30:00+10:00' - expected.period_start = '2015-01-17T16:00:00+10:00' + expected.period_end = '2015-01-17T16:30:00Z' + expected.period_start = '2015-01-17T16:00:00Z' expected.has_participant = true expected.location_display = 'Client\'s home' // encounterType: string | undefined @@ -56,6 +56,11 @@ describe('EncounterModel', () => { expected.encounter_type = [ { coding: [ Object({ system: 'http://snomed.info/sct', code: '11429006', display: 'Consultation' }) ] } ] expected.resource_class = 'ambulatory' expected.resource_status = 'finished' + expected.reasonCode = [ + { + text: 'The patient had fever peaks over the last couple of days. He is worried about these peaks.' + } + ] expected.participant = [ { display: undefined, reference: Object({ reference: 'Practitioner/f201' }), diff --git a/frontend/src/lib/models/resources/organization-model.ts b/frontend/src/lib/models/resources/organization-model.ts index 15edd369..0985a6b0 100644 --- a/frontend/src/lib/models/resources/organization-model.ts +++ b/frontend/src/lib/models/resources/organization-model.ts @@ -13,7 +13,8 @@ export class OrganizationModel extends FastenDisplayModel { name: string|undefined addresses: AddressModel[]|undefined telecom: { system?: string, value?: string, use?: string }[]|undefined - type_codings: any[]|undefined + type: CodableConceptModel[]|undefined + type_codings: CodingModel[]|undefined constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) { super(fastenOptions) @@ -27,6 +28,8 @@ export class OrganizationModel extends FastenDisplayModel { this.name = _.get(fhirResource, 'name'); this.addresses = _.get(fhirResource, 'address'); this.telecom = _.get(fhirResource, 'telecom'); + this.type = _.get(fhirResource, 'type'); + }; dstu2DTO(fhirResource:any){ this.type_codings = _.get(fhirResource, 'type.coding'); diff --git a/frontend/src/lib/models/resources/practitioner-model.ts b/frontend/src/lib/models/resources/practitioner-model.ts index 225c0dc6..66df1c07 100644 --- a/frontend/src/lib/models/resources/practitioner-model.ts +++ b/frontend/src/lib/models/resources/practitioner-model.ts @@ -20,9 +20,9 @@ export class PractitionerModel extends FastenDisplayModel { relationship: string }|undefined telecom: { system?: string, value?: string, use?: string }[]|undefined - address: AddressModel|undefined + address: AddressModel[]|undefined birthdate: string|undefined - qualification: { code: string, system: string }[]|undefined + qualification: CodingModel[]|undefined constructor(fhirResource: any, fhirVersion?: fhirVersions, fastenOptions?: FastenOptions) { super(fastenOptions) @@ -51,7 +51,7 @@ export class PractitionerModel extends FastenDisplayModel { stu3DTO(fhirResource:any){ this.name = _.get(fhirResource, 'name',[]).map((name:any): HumanNameModel => new HumanNameModel(name)); - this.address = new AddressModel(_.get(fhirResource, 'address.0')); + this.address = _.get(fhirResource, 'address', []).map((address:any): AddressModel => new AddressModel(address)); this.telecom = _.get(fhirResource, 'telecom'); }; diff --git a/frontend/src/lib/utils/bundle_references.ts b/frontend/src/lib/utils/bundle_references.ts new file mode 100644 index 00000000..2237fadc --- /dev/null +++ b/frontend/src/lib/utils/bundle_references.ts @@ -0,0 +1,43 @@ +import {FastenDisplayModel} from '../models/fasten/fasten-display-model'; +import {Reference, Resource} from 'fhir/r4'; + +//this function is used to generate an id to a resource within this bundle +export function internalResourceReferenceUri(resourceType: string, resourceId: string): string { + return `${resourceType}/${resourceId}` +} + +//this function is used to generate an id to a resource outside this bundle, but owned by the same user in another source +export function externalFastenResourceReferenceUri(sourceId: string, resourceType: string, resourceId: string): string { + return `urn:fastenhealth-fhir:${sourceId}:${resourceType}/${resourceId}` +} + +export function generateReferenceUriFromResourceOrReference(displayModelOrResourceOrReference: FastenDisplayModel | Resource | Reference): string { + if((displayModelOrResourceOrReference as Reference).reference) { + //reference from external source, eg. urn:fastenhealth-id:0000-0000-0000-0000:${source_resource_type}/${source_resource_id} + return (displayModelOrResourceOrReference as Reference).reference + } else if ( + (displayModelOrResourceOrReference as FastenDisplayModel).source_id && + (displayModelOrResourceOrReference as FastenDisplayModel).source_resource_type && + (displayModelOrResourceOrReference as FastenDisplayModel).source_resource_id + ) { + //reference from external source -- in display model format, eg. urn:fastenhealth-id:${source_id}:${source_resource_type}/${source_resource_id} + return `${externalFastenResourceReferenceUri( + (displayModelOrResourceOrReference as FastenDisplayModel).source_id, + (displayModelOrResourceOrReference as FastenDisplayModel).source_resource_type, + (displayModelOrResourceOrReference as FastenDisplayModel).source_resource_id + )}` + + } else if( + (displayModelOrResourceOrReference as FastenDisplayModel).source_resource_type && + (displayModelOrResourceOrReference as FastenDisplayModel).source_resource_id) { + //internal reference (within this bundle), eg. urn:uuid:${resource.id} + return `${internalResourceReferenceUri((displayModelOrResourceOrReference as FastenDisplayModel).source_resource_type, (displayModelOrResourceOrReference as FastenDisplayModel).source_resource_id)}` + } else if((displayModelOrResourceOrReference as Resource).id && (displayModelOrResourceOrReference as Resource).resourceType) { + //internal reference (within this bundle), eg. urn:uuid:${resource.id} + return `${internalResourceReferenceUri((displayModelOrResourceOrReference as Resource).resourceType, (displayModelOrResourceOrReference as Resource).id)}` + } else { + console.warn("Cannot determine resource id, this should not happen", displayModelOrResourceOrReference) + throw new Error("Cannot find resource id") + } +} + diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 631b1c62..b50c48b9 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -9574,6 +9574,11 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-full-name@^1.2.6: + version "1.2.6" + resolved "https://registry.npmjs.org/parse-full-name/-/parse-full-name-1.2.6.tgz#97e2643c0167b79ca404dab254ea79e89b025704" + integrity sha512-uIaENXJFmZfzulBndhHJayi7ZEifJ1bXKaWYmySa04EmMX7eIcsufiAgWTYiJqWRa/Sq7JWPGtCIXFAoUfF7gw== + parse-json@^5.0.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" diff --git a/go.mod b/go.mod index 70e22972..761ad6d7 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/dave/jennifer v1.6.1 github.com/dominikbraun/graph v0.15.0 github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 - github.com/fastenhealth/fasten-sources v0.4.16 + github.com/fastenhealth/fasten-sources v0.4.18 github.com/fastenhealth/gofhir-models v0.0.6 github.com/gin-gonic/gin v1.9.0 github.com/go-gormigrate/gormigrate/v2 v2.1.1 @@ -30,15 +30,16 @@ require ( github.com/spf13/viper v1.12.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.11.2 - golang.org/x/crypto v0.12.0 + golang.org/x/crypto v0.14.0 golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 - golang.org/x/net v0.14.0 + golang.org/x/net v0.17.0 gorm.io/datatypes v1.0.7 gorm.io/driver/sqlite v1.5.4 gorm.io/gorm v1.25.4 ) require ( + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.4.3 // indirect @@ -101,9 +102,9 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.66.4 // indirect diff --git a/go.sum b/go.sum index 6ee178f0..231ff15f 100644 --- a/go.sum +++ b/go.sum @@ -101,8 +101,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fastenhealth/fasten-sources v0.4.16 h1:2pfl2Ozi7nuVx3wBXrZhYnpM7hIErYkX0FBvC+JHvP8= -github.com/fastenhealth/fasten-sources v0.4.16/go.mod h1:ZO/X0+LE6gVp26mqm1oqnGePK8s9tniwuao4L3HPPiI= +github.com/fastenhealth/fasten-sources v0.4.18 h1:EAibtlSlz8m6+L0faYAANRATOV88D4AE7rdgLHF3nrE= +github.com/fastenhealth/fasten-sources v0.4.18/go.mod h1:tnvfgYG9utKCQ18+Jp26QBOO1ncwAkYx2l/XOBJqnyw= github.com/fastenhealth/gofhir-models v0.0.6 h1:yJYYaV1eJtHiGEfA1rXLsyOm/9hIi6s2cGoZzGfW1tM= github.com/fastenhealth/gofhir-models v0.0.6/go.mod h1:xB8ikGxu3bUq2b1JYV+CZpHqBaLXpOizFR0eFBCunis= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -111,6 +111,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= @@ -455,8 +457,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -533,8 +535,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -616,15 +618,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -636,8 +638,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=