Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Release Oswego v0.15 #553

Merged
merged 51 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c8f488f
fix employee information form
AykhanAhmadli Oct 21, 2024
fdbf768
Add questionnaire responses to additional questions patient column in…
oliviabonitatibus Oct 15, 2024
f9586db
refactor error logic
AykhanAhmadli Oct 21, 2024
4bc7a86
add connection naming
rzinger Oct 22, 2024
f48a442
adding missing effects
rzinger Oct 22, 2024
5bd0a01
remove inputs once qualification is added
AykhanAhmadli Oct 23, 2024
49c1f04
make connection name optional
rzinger Oct 23, 2024
351d58f
for now, do not include connection name
rzinger Oct 23, 2024
de54852
adding connection details back in
rzinger Oct 23, 2024
ebf6e86
need to keep this suppressed for now
rzinger Oct 23, 2024
251ef9b
Merge pull request #515 from masslight/508/erx-enrollment
rzinger Oct 23, 2024
6eb64cd
Merge pull request #519 from masslight/main
saewitz Oct 24, 2024
121d7a6
fix ottehr deploy script
AykhanAhmadli Oct 28, 2024
4af7630
Merge pull request #528 from masslight/aykhan/ehr-deploy
AykhanAhmadli Oct 28, 2024
abdf813
Merge branch 'develop' into aykhan/provider-details
AykhanAhmadli Oct 28, 2024
da7706d
stashing changes
GiladSchneider Oct 29, 2024
4b5042b
cancelled appointments show up in cancelled tab
GiladSchneider Oct 29, 2024
f838b0a
Merge pull request #531 from masslight/505/cancelled-tab
GiladSchneider Oct 29, 2024
30335f9
Merge branch 'develop' into 526/fix-appointment-status
GiladSchneider Oct 29, 2024
8b85777
more stashing
GiladSchneider Oct 29, 2024
9c72edc
progress
GiladSchneider Oct 29, 2024
0a8b56e
change submit logic and error handling
AykhanAhmadli Oct 30, 2024
b57b149
allows editing appointment status for in person appointments
GiladSchneider Oct 30, 2024
7cecc9e
telemed tab debugging
GiladSchneider Oct 30, 2024
7a18cd7
removes console logs and fixes telemedicine tab bug
GiladSchneider Oct 30, 2024
ca37961
fix for get patients errors
AykhanAhmadli Oct 30, 2024
57e8b9d
feat: add loading indicator for patients data fetching
AykhanAhmadli Oct 30, 2024
22438e9
revert auth changes
AykhanAhmadli Oct 31, 2024
d24cd5e
remove unhandled lincese state
AykhanAhmadli Oct 31, 2024
d977a85
feat: fix add patient page
AykhanAhmadli Oct 31, 2024
32e81eb
fixes zambda
GiladSchneider Oct 31, 2024
432cee5
remove horizontal rule
AykhanAhmadli Oct 31, 2024
bf5e900
Merge pull request #539 from masslight/aykhan/add-patient
AykhanAhmadli Oct 31, 2024
feaa1b2
Merge pull request #538 from masslight/continue-button
AykhanAhmadli Oct 31, 2024
de72437
slight cleanup
GiladSchneider Nov 1, 2024
a6722d5
fixed build
GiladSchneider Nov 1, 2024
f07d66b
merged components'
GiladSchneider Nov 1, 2024
190ba0e
add required
AykhanAhmadli Nov 1, 2024
ca30064
Merge pull request #500 from masslight/aykhan/provider-details
AykhanAhmadli Nov 1, 2024
5f79542
resolved comments
GiladSchneider Nov 1, 2024
c5adc61
Merge pull request #542 from masslight/526/fix-appointment-status
GiladSchneider Nov 1, 2024
59c3c2e
edit chat modal and fix for appointment side panel
AykhanAhmadli Nov 1, 2024
3d7c8a7
fix exam
AykhanAhmadli Nov 1, 2024
675d701
fix build
AykhanAhmadli Nov 4, 2024
7502e73
wip: attempt to remove lint error
AykhanAhmadli Nov 4, 2024
bda64d7
bumped version to 0.15
GiladSchneider Nov 4, 2024
7d77d18
Merge pull request #547 from masslight/0.15-version-bump
GiladSchneider Nov 4, 2024
96c3de4
wip: workaround for exam info
AykhanAhmadli Nov 4, 2024
b151d77
reverting fix attempt for exam cards
AykhanAhmadli Nov 4, 2024
749d78f
Merge pull request #543 from masslight/aykhan/fix-soap-sms
AykhanAhmadli Nov 4, 2024
04c4ad9
Merge pull request #501 from Harbor-Systems/additional_questionnaire_…
GiladSchneider Nov 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ottehr",
"version": "0.14.0",
"version": "0.15.0",
"private": true,
"scripts": {
"test": "pnpm recursive run test",
Expand Down
63 changes: 63 additions & 0 deletions packages/ehr-utils/lib/fhir/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import {
OTTEHR_PATIENT_MESSAGE_CODE,
OTTEHR_PATIENT_MESSAGE_SYSTEM,
OttehrPatientMessageStatus,
RelatedPersonMaps,
SMSModel,
SMSRecipient,
} from '../types';
import { BatchInputRequest, FhirClient, User } from '@zapehr/sdk';
import { DateTime } from 'luxon';
Expand Down Expand Up @@ -41,6 +44,52 @@ export const getChatContainsUnreadMessages = (chat: Communication[]): boolean =>
return readStatusList.find((stat) => stat === false) !== undefined;
};

export const getCommunicationsAndSenders = async (
fhirClient: FhirClient,
uniqueNumbers: string[],
): Promise<(Communication | RelatedPerson)[]> => {
return await fhirClient.searchResources<Communication | RelatedPerson>({
resourceType: 'Communication',
searchParams: [
{ name: 'medium', value: ZAP_SMS_MEDIUM_CODE },
{ name: 'sender:RelatedPerson.telecom', value: uniqueNumbers.join(',') },
{ name: '_include', value: 'Communication:sender:RelatedPerson' },
],
});
};

export function getUniquePhonesNumbers(allRps: RelatedPerson[]): string[] {
const uniquePhoneNumbers: string[] = [];

allRps.forEach((rp) => {
const phone = getSMSNumberForIndividual(rp);
if (phone && !uniquePhoneNumbers.includes(phone)) uniquePhoneNumbers.push(phone);
});

return uniquePhoneNumbers;
}

export const createSmsModel = (patientId: string, allRelatedPersonMaps: RelatedPersonMaps): SMSModel | undefined => {
let rps: RelatedPerson[] = [];
try {
rps = allRelatedPersonMaps.rpsToPatientIdMap[patientId];
const recipients = filterValidRecipients(rps);
if (recipients.length) {
const allComs = recipients.flatMap((recip) => {
return allRelatedPersonMaps.commsToRpRefMap[recip.relatedPersonId] ?? [];
});
return {
hasUnreadMessages: getChatContainsUnreadMessages(allComs),
recipients,
};
}
} catch (e) {
console.log('error building sms model: ', e);
console.log('related persons value prior to error: ', rps);
}
return undefined;
};

export interface MakeOttehrMessageReadStatusInput {
userId: string;
timeRead: string;
Expand Down Expand Up @@ -148,3 +197,17 @@ export const initialsFromName = (name: string): string => {
});
return parts.join('');
};

function filterValidRecipients(relatedPersons: RelatedPerson[]): SMSRecipient[] {
// some slack alerts suggest this could be undefined, but that would mean there are patients with no RP
// or some bug preventing rp from being returned with the query
return relatedPersons
.map((rp) => {
return {
recipientResourceUri: rp.id ? `RelatedPerson/${rp.id}` : undefined,
smsNumber: getSMSNumberForIndividual(rp),
relatedPersonId: rp.id,
};
})
.filter((rec) => rec.recipientResourceUri !== undefined && rec.smsNumber !== undefined) as SMSRecipient[];
}
57 changes: 56 additions & 1 deletion packages/ehr-utils/lib/fhir/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BatchInputRequest, FhirClient } from '@zapehr/sdk';
import { Operation } from 'fast-json-patch';
import { Coding, Patient, Person, Practitioner, RelatedPerson, Resource, Appointment } from 'fhir/r4';
import { Coding, Patient, Person, Practitioner, RelatedPerson, Resource, Appointment, Extension } from 'fhir/r4';
import { FHIR_EXTENSION } from './constants';
export * from './chat';

Expand All @@ -27,6 +27,10 @@ export const getFullestAvailableName = (
return undefined;
};

export function filterResources(allResources: Resource[], resourceType: string): Resource[] {
return allResources.filter((res) => res.resourceType === resourceType && res.id);
}

export const getPatchOperationForNewMetaTag = (resource: Resource, newTag: Coding): Operation => {
if (resource.meta == undefined) {
return {
Expand Down Expand Up @@ -70,6 +74,57 @@ export const getPatchOperationForNewMetaTag = (resource: Resource, newTag: Codin
}
};

export const getPatchOperationToUpdateExtension = (
resource: { extension?: Extension[] },
newExtension: {
url: Extension['url'];
valueString?: Extension['valueString'];
valueBoolean?: Extension['valueBoolean'];
},
): Operation | undefined => {
if (!resource.extension) {
return {
op: 'add',
path: '/extension',
value: [newExtension],
};
}

const extension = resource.extension;
let requiresUpdate = false;

if (extension.length > 0) {
const existingExtIndex = extension.findIndex((ext) => ext.url === newExtension.url);
// check if formUser exists and needs to be updated and if so, update
if (
existingExtIndex >= 0 &&
(extension[existingExtIndex].valueString !== newExtension.valueString ||
extension[existingExtIndex].valueBoolean !== newExtension.valueBoolean)
) {
extension[existingExtIndex] = newExtension;
requiresUpdate = true;
} else if (existingExtIndex < 0) {
// if form user does not exist within the extension
// push to patientExtension array
extension.push(newExtension);
requiresUpdate = true;
}
} else {
// since no extensions exist, it must be added via patch operations
extension.push(newExtension);
requiresUpdate = true;
}

if (requiresUpdate) {
return {
op: 'replace',
path: '/extension',
value: extension,
};
}

return undefined;
};
export interface GetPatchBinaryInput {
resourceId: string;
resourceType: string;
Expand Down
1 change: 1 addition & 0 deletions packages/ehr-utils/lib/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './formatPhoneNumber';
export * from './paperwork';
export * from './practitioner';
export * from './telemed-appointment.helper';
export * from './mappers';
99 changes: 99 additions & 0 deletions packages/ehr-utils/lib/helpers/mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { FhirClient } from '@zapehr/sdk';
import { Communication, RelatedPerson, Resource } from 'fhir/r4';
import { removePrefix } from '.';
import {
filterResources,
getCommunicationsAndSenders,
getSMSNumberForIndividual,
getUniquePhonesNumbers,
} from '../fhir';
import { RelatedPersonMaps } from '../types';

export const relatedPersonAndCommunicationMaps = async (
fhirClient: FhirClient,
inputResources: Resource[],
): Promise<RelatedPersonMaps> => {
const allRelatedPersons = filterResources(inputResources, 'RelatedPerson') as RelatedPerson[];
const rpsPhoneNumbers = getUniquePhonesNumbers(allRelatedPersons);
const rpsToPatientIdMap = mapRelatedPersonToPatientId(allRelatedPersons);
const rpToIdMap = mapRelatedPersonToId(allRelatedPersons);
const foundResources = await getCommunicationsAndSenders(fhirClient, rpsPhoneNumbers);
const foundRelatedPersons = filterResources(foundResources, 'RelatedPerson') as RelatedPerson[];
Object.assign(rpToIdMap, mapRelatedPersonToId(foundRelatedPersons));
rpsPhoneNumbers.concat(getUniquePhonesNumbers(foundRelatedPersons)); // do better here
const rpsRefsToPhoneNumberMap = mapRelatedPersonsRefsToPhoneNumber(foundRelatedPersons);

const foundCommunications = filterResources(foundResources, 'Communication') as Communication[];
const commsToRpRefMap = mapCommunicationsToRelatedPersonRef(foundCommunications, rpToIdMap, rpsRefsToPhoneNumberMap);

return {
rpsToPatientIdMap,
commsToRpRefMap,
};
};

function mapRelatedPersonToPatientId(allRps: RelatedPerson[]): Record<string, RelatedPerson[]> {
const rpsToPatientIdMap: Record<string, RelatedPerson[]> = {};

allRps.forEach((rp) => {
const patientId = removePrefix('Patient/', rp.patient.reference || '');
if (patientId) {
if (rpsToPatientIdMap[patientId]) rpsToPatientIdMap[patientId].push(rp);
else rpsToPatientIdMap[patientId] = [rp];
}
});

return rpsToPatientIdMap;
}

function mapRelatedPersonToId(allRps: RelatedPerson[]): Record<string, RelatedPerson> {
const rpToIdMap: Record<string, RelatedPerson> = {};

allRps.forEach((rp) => {
rpToIdMap['RelatedPerson/' + rp.id] = rp;
});

return rpToIdMap;
}

function mapRelatedPersonsRefsToPhoneNumber(allRps: RelatedPerson[]): Record<string, string[]> {
const relatedPersonRefToPhoneNumber: Record<string, string[]> = {};

allRps.forEach((rp) => {
const rpRef = `RelatedPerson/${rp.id}`;
const pn = getSMSNumberForIndividual(rp as RelatedPerson);
if (pn) {
if (relatedPersonRefToPhoneNumber[pn]) relatedPersonRefToPhoneNumber[pn].push(rpRef);
else relatedPersonRefToPhoneNumber[pn] = [rpRef];
}
});
return relatedPersonRefToPhoneNumber;
}

function mapCommunicationsToRelatedPersonRef(
allCommunications: Communication[],
rpToIdMap: Record<string, RelatedPerson>,
rpsRefsToPhoneNumberMap: Record<string, string[]>,
): Record<string, Communication[]> {
const commsToRpRefMap: Record<string, Communication[]> = {};

allCommunications.forEach((comm) => {
const communication = comm as Communication;
const rpRef = communication.sender?.reference;
if (rpRef) {
const senderResource = rpToIdMap[rpRef];
if (senderResource) {
const smsNumber = getSMSNumberForIndividual(senderResource);
if (smsNumber) {
const allRPsWithThisNumber = rpsRefsToPhoneNumberMap[smsNumber];
allRPsWithThisNumber.forEach((rpRef) => {
if (commsToRpRefMap[rpRef]) commsToRpRefMap[rpRef].push(communication);
else commsToRpRefMap[rpRef] = [communication];
});
}
}
}
});

return commsToRpRefMap;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ export const mapPaperworkResponseItem = (item: QuestionnaireResponseItem): Quest
return item as QuestionnaireResponseItemWithValueArray;
};

export enum QuestionnaireLinkIds {
PREFERRED_LANGUAGE = 'preferred-language',
ALLERGIES = 'allergies',
REASON_FOR_VISIT = 'reason-for-visit',
VITALS_TEMPERATURE = 'vitals-temperature',
VITALS_PULSE = 'vitals-pulse',
VITALS_HR = 'vitals-hr',
VITALS_RR = 'vitals-rr',
VITALS_BP = 'vitals-bp',
MEDICAL_HISTORY = 'medical-history',
SURGICAL_HISTORY = 'surgical-history',
CURRENT_MEDICATION = 'current-medications',
PATIENT_STREET_ADDRESS = 'patient-street-address',
PATIENT_NUMBER = 'patient-number',
GUARDIAN_NUMBER = 'guardian-number',
RELAY_PHONE = 'relay-phone',
CONSENT_FORMS = 'consent-forms',
}

export const getQuestionnaireResponseByLinkId = (
linkId: string,
questionnaireResponse?: QuestionnaireResponse,
Expand Down
2 changes: 2 additions & 0 deletions packages/ehr-utils/lib/types/appointment.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const mapStatusToTelemed = (
case 'finished':
if (appointmentStatus === 'fulfilled') return ApptStatus.complete;
else return ApptStatus.unsigned;
case 'cancelled':
return ApptStatus.cancelled;
}
return undefined;
};
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const PRIVATE_EXTENSION_BASE_URL = 'https://fhir.zapehr.com/r4/StructureDefiniti
const visitStatusExtensionCode = 'visit-history';
export const visitStatusExtensionUrl = `${PRIVATE_EXTENSION_BASE_URL}/${visitStatusExtensionCode}`;

const getStatusFromExtension = (resource: Appointment): VisitStatus | undefined => {
export const getStatusFromExtension = (resource: Appointment): VisitStatus | undefined => {
const history = getVisitStatusHistory(resource);
if (history) {
const historySorted = [...history]
Expand Down
14 changes: 14 additions & 0 deletions packages/ehr-utils/lib/types/user.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,17 @@ export type User = ZapEHRUser & {
roles: { name: string }[];
profileResource?: Practitioner;
};

export enum RoleType {
NewUser = 'NewUser',
Administrator = 'Administrator',
AssistantAdmin = 'AssistantAdmin',
RegionalTelemedLead = 'RegionalTelemedLead',
CallCentre = 'CallCentre',
Billing = 'Billing',
Manager = 'Manager',
Staff = 'Staff',
Provider = 'Provider',
FrontDesk = 'Front Desk',
Inactive = 'Inactive',
}
2 changes: 1 addition & 1 deletion packages/ehr-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ehr-utils",
"private": true,
"version": "0.14.0",
"version": "0.15.0",
"main": "lib/main.ts",
"types": "lib/main.ts",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/ottehr-components/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ottehr-components",
"private": true,
"version": "0.14.0",
"version": "0.15.0",
"main": "lib/main.ts",
"types": "lib/main.ts",
"scripts": {
Expand Down
6 changes: 5 additions & 1 deletion packages/telemed-ehr/app/env/.env.local-template
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ VITE_APP_GET_APPOINTMENTS_ZAMBDA_ID=get-appointments
VITE_APP_CREATE_APPOINTMENT_ZAMBDA_ID=create-appointment
VITE_APP_UPDATE_USER_ZAMBDA_ID=update-user
VITE_APP_GET_USER_ZAMBDA_ID=get-user
VITE_APP_GET_SCHEDULE_ZAMBDA_ID=get-schedule
VITE_APP_SYNC_USER_ZAMBDA_ID=sync-user
VITE_APP_DEACTIVATE_USER_ZAMBDA_ID=deactivate-user
VITE_APP_GET_EMPLOYEES_ZAMBDA_ID=get-employees
Expand All @@ -30,4 +31,7 @@ VITE_APP_DELETE_CHART_DATA_ZAMBDA_ID=delete-chart-data
VITE_APP_GET_TOKEN_FOR_CONVERSATION_ZAMBDA_ID=get-token-for-conversation
VITE_APP_CANCEL_TELEMED_APPOINTMENT_ZAMBDA_ID=cancel-telemed-appointment
VITE_APP_CANCEL_IN_PERSON_APPOINTMENT_ZAMBDA_ID=cancel-in-person-appointment
VITE_APP_QRS_URL=http://localhost:3002
VITE_APP_QRS_URL=http://localhost:3002
VITE_APP_PHOTON_ORG_ID=
VITE_APP_PHOTON_CLIENT_ID=
VITE_APP_PHOTON_CONNECTION_NAME=
4 changes: 2 additions & 2 deletions packages/telemed-ehr/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "telemed-ehr-app",
"version": "0.14.0",
"version": "0.15.0",
"private": true,
"browserslist": {
"production": [
Expand Down Expand Up @@ -30,7 +30,7 @@
"test": "react-scripts test",
"eject": "react-scripts eject",
"deploy:development": " PREFIX=development CLOUDFRONT_ID=E10TA6FN58D1OS ENV=development pnpm run ci-deploy-skeleton",
"ci-deploy-skeleton": "ENV=${ENV} VITE_APP_SHA=${GIT_HEAD:-$(git rev-parse --short HEAD)} VITE_APP_VERSION=$(node -pe 'require(\"./package.json\").version') pnpm run build:${ENV} && aws s3 sync build/ s3://ehr.ottehr.com --region us-east-1 --delete --profile ottehr && aws cloudfront create-invalidation --distribution-id ${CLOUDFRONT_ID} --paths '/*' --region us-east-1"
"ci-deploy-skeleton": "ENV=${ENV} VITE_APP_SHA=${GIT_HEAD:-$(git rev-parse --short HEAD)} VITE_APP_VERSION=$(node -pe 'require(\"./package.json\").version') pnpm run build:${ENV} && aws s3 sync build/ s3://ehr.ottehr.com --region us-east-1 --delete && aws cloudfront create-invalidation --distribution-id ${CLOUDFRONT_ID} --paths '/*' --region us-east-1"
},
"dependencies": {
"@mui/icons-material": "^5.14.9",
Expand Down
1 change: 1 addition & 0 deletions packages/telemed-ehr/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ function App(): ReactElement {
dev-mode="true"
auto-login="true"
redirect-uri={window.location.origin}
// connection={import.meta.env.VITE_APP_PHOTON_CONNECTION_NAME}
>
<Outlet />
</photon-client>
Expand Down
Loading
Loading