From 0a287177efa995ca225d45fba5433c95b8be4498 Mon Sep 17 00:00:00 2001 From: Roopak A N Date: Mon, 7 Oct 2024 16:35:35 +0530 Subject: [PATCH 1/4] add scheduling docs --- docs/care/CEP/Draft/0007-scheduling/Design.md | 157 ++++++++++++++++++ .../index.md} | 0 .../CEP/Draft/0007-scheduling/scheduling.png | Bin 0 -> 21264 bytes 3 files changed, 157 insertions(+) create mode 100644 docs/care/CEP/Draft/0007-scheduling/Design.md rename docs/care/CEP/Draft/{0007-scheduling.md => 0007-scheduling/index.md} (100%) create mode 100644 docs/care/CEP/Draft/0007-scheduling/scheduling.png diff --git a/docs/care/CEP/Draft/0007-scheduling/Design.md b/docs/care/CEP/Draft/0007-scheduling/Design.md new file mode 100644 index 0000000..cb626f1 --- /dev/null +++ b/docs/care/CEP/Draft/0007-scheduling/Design.md @@ -0,0 +1,157 @@ +# Backend Design + +## Design Thesis + +Each resource - be it doctor, OT, any other facility, including consultation room - will be bookable and have their respective availability charts. + +### Availability and Scheduling +- Make booking and schedule agnostic to business logic. +- Use a factory model design to add specific business logic. + +### Live Queue +- Live Queue will follow a priority score system → a score will be assigned to every booking, depending on multiple factors, and a simple sort on this should allow live queue + +## Models + +### Availability +All resources which can be scheduled shall have an entry into the `SchedulableResource` model. It will be linked back to the original object as well +```py +class SchedulableResource: + id: int + type: enum - "doctor" | "ot" | "lab" | "consultation_room" + + object_id: int # use actual id of the object. +``` + +The availability of a resource will be stored using 3 models - `AvailabilityConfiguration`, `Availability` and `AvailabilityException`. + +The `AvailabilityConfiguration` will be created each time a new configuration is added. When a new configuration is setup, the old one is invalidated by setting the `valid_till` and creating a new configuration. + +```py +class AvailabilityConfiguration: + resource_id: int # foreign key to SchedulableResource + valid_from: datetime + valid_till: datetime + slot_size_in_minutes: int # number of minutes in a slot + tokens_per_slot: int # for OT, Rooms, it will be 1, but for doctors, it can be 10 tokens per 30 minutes, etc. + +class Availability: + configuration_id: int # fk to AvailabilityConfiguration + # can have more than one entry for the same day to support 10-12 and 2-4 + day_of_the_week: int # 0 - 6; Monday is 0 and Sunday is 6. As per Python documentation https://docs.python.org/3/library/datetime.html#datetime.datetime.weekday + start_time: time + end_time + +class AvailabilityException: + resource_id: int + + is_avaiable: bool + # exception can be being available on a non-configured day, + # as well as being not-available on a configured day + + start_datetime + end_datetime + reason # [optional] national holiday, doctor on leave, etc. +``` + +:::warning[Availability as a separate service] +Availability shall be written as a separate service, and all data access should be driven through the service alone. Direct calls to the models should not be allowed. We can achieve this through reviews and try using pre-commit hooks. +::: + +### Booking +```py +class Booking: + patient_id + start_datetime + end_datetime + booked_by + status: enum "requested" | "approved" | "denied" | "canceled" + status_message: str # to be used in case requested bookings were denied, or canceled. + + resources_used: list[BookingResource] # one to many relation with BookingResources + resources_used_json: json # processed json - only for read + +class BookingResource: + booking_id + resource_id + + # separate datetime allows for the provision of some doctors + # being available only for the first few minutes / hours. + start_datetime + end_datetime +``` + +### Dynamic booking vs Slot based booking + +:::info +Generating slots based on availability was one of the first ideas which was discussed. It required a cron job and an asynchronous task to generate slot and handle changes to configuration. + +It was then decided to proceed with a dynamic booking approach as it required less infrastructure and the scale was not identified to be so much. The dynamic mechanism discussed below will use DB row level locking and table based locking mechanism to avoid cross booking. +::: + +**Implementing DB based locking** + +```py +class ScheduleLock: + resource_id: + is_locked: boolean + locked_at: nullable timestamp +``` + +![Scheduling Logic](./scheduling.png) + +:::note +A resource is schedulable if the `is_locked` is `False` OR `is_locked` is `True` but `locked_at` timestamp is older than 1 minute. The 1 minute is the timeframe within which the resource scheduling should be completed by the API. +::: + +#### Sample algorithm for Booking +```py +for resource in resources: + ResourceLock.objects.get_or_create( + resource_id=resource.id, + defaults={ + "is_locked": False, + "locked_at": None, + }, + ) + + try: + with transaction.atomic(): + resources_to_lock = ( + ResourceLock.objects.filter( + resource_id__in=resource_ids_to_lock, + ) + .filter( + Q(is_locked=False) + | Q( + is_locked=True, + locked_at__lt=timezone.now() - timedelta(minutes=1), + ) + ) + .select_for_update(nowait=True, skip_locked=False) + ) + if resources_to_lock.count() != len(resource_ids_to_lock): + raise ValueError("Resource is locked.") + resources_to_lock.update(is_locked=True, locked_at=timezone.now() + ) + except ValueError: + raise Exception(“select_for_update failed”) +``` + +## Operations +Following are the operations allowed on the Availability Service +- Add / Update Availability + - For doctors, admin, other staff members + - Permissions, delegations need to be handled. + - We can consider a delegation framework. +- Get Schedule +- Lists the availability +- Check Availability +- Check availability during a time slot ? +- Schedule + - Request for a schedule + - Helps to handle requests from the public. + - Book a schedule + - Either approve a request or create a new one. +- Approval, Creating an approved schedule to be handled based on permissions. + diff --git a/docs/care/CEP/Draft/0007-scheduling.md b/docs/care/CEP/Draft/0007-scheduling/index.md similarity index 100% rename from docs/care/CEP/Draft/0007-scheduling.md rename to docs/care/CEP/Draft/0007-scheduling/index.md diff --git a/docs/care/CEP/Draft/0007-scheduling/scheduling.png b/docs/care/CEP/Draft/0007-scheduling/scheduling.png new file mode 100644 index 0000000000000000000000000000000000000000..fb525ebd284218bce51fa74f8f0f6c1a9d1e52c9 GIT binary patch literal 21264 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 E0KmUeNdN!< literal 0 HcmV?d00001 From c95536dc55c22cd5c534920aa01ff251c2e34d0e Mon Sep 17 00:00:00 2001 From: Roopak A N Date: Mon, 14 Oct 2024 18:32:17 +0530 Subject: [PATCH 2/4] implementing review comments and moving to mermaid charts --- docs/care/CEP/Draft/0007-scheduling.md | 268 ++++++++++++++++++ docs/care/CEP/Draft/0007-scheduling/Design.md | 157 ---------- docs/care/CEP/Draft/0007-scheduling/index.md | 96 ------- .../CEP/Draft/0007-scheduling/scheduling.png | Bin 21264 -> 0 bytes 4 files changed, 268 insertions(+), 253 deletions(-) create mode 100644 docs/care/CEP/Draft/0007-scheduling.md delete mode 100644 docs/care/CEP/Draft/0007-scheduling/Design.md delete mode 100644 docs/care/CEP/Draft/0007-scheduling/index.md delete mode 100644 docs/care/CEP/Draft/0007-scheduling/scheduling.png diff --git a/docs/care/CEP/Draft/0007-scheduling.md b/docs/care/CEP/Draft/0007-scheduling.md new file mode 100644 index 0000000..eaaa10b --- /dev/null +++ b/docs/care/CEP/Draft/0007-scheduling.md @@ -0,0 +1,268 @@ +# CEP-7: Scheduling in CARE + +### Motive + +Care is being used in different healthcare environments for various use-cases, including consultation scheduling, OT scheduling, vaccination appointment scheduling, and other appointment management needs. A robust and flexible scheduling system is essential for efficient healthcare delivery and resource management. + +### Requirements + +### 1. Appointment Booking + +- The system shall allow staff members to book appointments for patients. +- Appointments can be scheduled for future dates or as walk-ins for the current day. +- The system shall support different appointment types, including: + - New patient appointments + - Follow-up appointments +- The system shall allow doctors to specify days for specific appointment types (e.g., follow-ups only on certain days). +- Appointments may include multiple participants or be linked to other objects. For example, a procedure could be an object with a team associated with it, and the team would consist of the participants involved in the appointment. +- Future Scope: The system shall provide a public-facing webpage where users can book their own appointments. + +### 2. Resource Management + +- Doctors shall be considered as schedulable resources within the system. +- Doctors shall have the ability to manage their own schedules. +- The system shall allow authorized staff members to manage doctors' schedules on their behalf. +- The system shall support flexible scheduling options, including: + - Fixed slot durations (e.g., 15-minute intervals) + - Daily patient limits (e.g., maximum of 20 patients per day) + +### 3. Availability Management + +- The system shall have the capability to show availability of resources (doctors, rooms, equipment, etc.). +- Doctors shall be able to set their available days and times. +- Doctors shall have the option to mark certain periods as unavailable. +- Doctors shall be able to specify which days in the future they can be booked. +- The system shall respect all availability settings when allowing appointments to be booked. +- The system shall support recurring availability patterns (e.g., available every Monday and Wednesday, 9 AM to 5 PM). +- Authorized staff members shall be able to view and modify availability settings on behalf of doctors, subject to appropriate permissions. + +### 4. Check-in and Encounter Management + +- The system shall provide a check-in function for when patients arrive at the facility. +- An encounter shall only be created in the system upon patient check-in. +- The system shall automatically manage a token system for patient queuing. (mix of appointments + walk-ins) [Logic is an open challenge to be tackled with Roopak] +- The system shall implement a check-in based priority system that automatically manages appointments and walk-ins. +- The check-in process shall update the patient's queue position based on their appointment time and arrival time. + +### 5. Priority Management + +- The system shall support different levels of patient priority. +- Staff shall have the ability to prioritize a VIP patient in the queue. +- The system shall provide clear visibility of the current queue order to staff members. +- The priority management system shall be flexible enough to accommodate emergency situations. + +### 6. Appointment Statuses + +- The system shall support multiple statuses for scheduled visits, including but not limited to: + - Scheduled + - Checked In + - In Progress + - Completed + - Cancelled + - No Show +- The system shall allow for status updates throughout the appointment lifecycle. + +### 7. Location-based Scheduling + +- The system shall support the registration and booking of various locations, including but not limited to Operating Theaters. +- This implementation shall be generic, allowing extension to support other types of location-based scheduling (e.g., vaccination appointment scheduling). +- Users with appropriate access rights shall be able to book locations. +- Location schedules shall be manageable by designated managers (can be the owner of the location schedule). +- The system shall support the handling of emergency cases that may affect location schedules. + +### 8. User Permissions + +- The system shall implement role-based access control for scheduling functions. +- Specific permissions shall be required for managing doctors' schedules on their behalf. +- Location booking shall be restricted to authorized doctors and staff members. + +### 9. Flexibility and Customization + +- The system shall allow for customization of appointment durations based on doctor or appointment type preferences. +- The system shall support different scheduling rules for different departments or specialties. + +### 10. Notifications + +- The system shall send notifications to relevant staff members for schedule changes, new bookings, and cancellations. +- Future: Allow SMS and whatsapp confirmations for patients + +### 11. Audit Trail + +- The system shall maintain a comprehensive audit trail of all scheduling actions, including creations, modifications, and cancellations. +- It shall support comments that can be added by users with access to the object. + +### 12. Future Beckn Integration + +- The architecture shall be flexible enough to accommodate Beckn protocols and standards for interoperable scheduling. + + +## Backend Design + +### Design Thesis + +Each resource - be it doctor, OT, any other facility, including consultation room - will be bookable and have their respective availability charts. + +### Availability and Scheduling +- Make booking and schedule agnostic to business logic. +- Use a factory model design to add specific business logic. + +### Live Queue +- Live Queue will follow a priority score system → a score will be assigned to every booking, depending on multiple factors, and a simple sort on this should allow live queue + +## Models + +### Availability +All resources which can be scheduled shall have an entry into the `SchedulableResource` model. It will be linked back to the original object as well +```py +class SchedulableResource: + id: int + type: enum - "doctor" | "ot" | "lab" | "consultation_room" + + object_id: int # use actual id of the object. +``` + +The availability of a resource will be stored using 3 models - `Schedule`, `Availability` and `AvailabilityException`. + +The `Schedule` will be created each time a new configuration is added. When a new configuration is setup, the old one is invalidated by setting the `valid_till` and creating a new configuration. + +```py +class Schedule: + resource_id: int # foreign key to SchedulableResource + valid_from: datetime + valid_till: datetime + slot_size_in_minutes: int # number of minutes in a slot + tokens_per_slot: int # for OT, Rooms, it will be 1, but for doctors, it can be 10 tokens per 30 minutes, etc. + +class Availability: + schedule_id: int # fk to Schedule + # can have more than one entry for the same day to support 10-12 and 2-4 + day_of_the_week: int # 0 - 6; Monday is 0 and Sunday is 6. As per Python documentation https://docs.python.org/3/library/datetime.html#datetime.datetime.weekday + start_time: time + end_time + +class AvailabilityException: + resource_id: int + + is_avaiable: bool + # exception can be being available on a non-configured day, + # as well as being not-available on a configured day + + start_datetime + end_datetime + reason # [optional] national holiday, doctor on leave, etc. +``` + +:::warning[Availability as a separate service] +Availability shall be written as a separate service, and all data access should be driven through the service alone. Direct calls to the models should not be allowed. We can achieve this through reviews and try using pre-commit hooks. +::: + +### Booking +```py +class Booking: + patient_id + start_datetime + end_datetime + booked_by + status: enum "requested" | "approved" | "denied" | "canceled" + status_message: str # to be used in case requested bookings were denied, or canceled. + + # resources_used: list[BookingResource] # one to many relation with BookingResource + +class BookingResource: + booking_id + resource_id + + # separate datetime allows for the provision of some doctors + # being available only for the first few minutes / hours. + start_datetime + end_datetime +``` + +:::warning DB level constraints to avoid overbooking +DB level constraint shall be added to `BookingResource` table, which will check if the resource is being used in another `Booking` +::: + +### Dynamic booking vs Slot based booking + +:::info +Generating slots based on availability was one of the first ideas which was discussed. It required a cron job and an asynchronous task to generate slot and handle changes to configuration. + +It was then decided to proceed with a dynamic booking approach as it required less infrastructure and the scale was not identified to be so much. The dynamic mechanism discussed below will use DB row level locking and table based locking mechanism to avoid cross booking. +::: + +**Implementing locking** + +- Locking will be implemented using Redis locks. +- All locks to have a maximum of 1 min of TTL. This is to avoid holding of locks even if the API crashes. + + +```mermaid +--- +title: Scheduling logic +--- +flowchart TD + start(((Start))) --> sReq["Schedule Request"] + sReq --> vReqdata["Validate Basic Request Data"] + vReqdata --> vAvailability["Validate Basic Request Data"] + vAvailability --> lockCheck{"Can acquire all locks?"} + lockCheck --> |Yes| sc["Schedule"] + sc --> rel["Release all acquired locks"] + rel --> stop((Stop)) + + lockCheck --> |No| relWhatever["Release any of the acquired locks"] + relWhatever --> stop +``` + +### Token Booking +Token booking and Booking needs to be handled seperately as Booking of resources needs to override the tokens booked. + +```py +class TokenSlot: + resource_id + tokens_remaining + start_datetime + end_datetime + +class TokenBooking: + slot_id # FK to Slot + patient_id + start_datetime + end_datetime + booked_by + status: enum "requested" | "approved" | "denied" | "canceled" + status_message: str # to be used in case requested bookings were denied, or canceled. +``` + +**Sample Algorithm** +```mermaid +--- +title: Token booking logic +--- +flowchart TD + start(((Start))) --> getSlot["Get Slot data based on the Schedule"] + getSlot --> createSlot["get_or_create the slot with tokens_remaining as max tokens per slot"] + createSlot --> lockSlot{"do row level locking of slot + before creating a token booking"} + lockSlot --> |Locked| bookToken["Create token booking"] --> stop(((Stop))) + lockSlot --> |Lock Failed| Wait --> |Try to lock again| lockSlot + Wait --> |Exception| stop +``` + + +## Operations +Following are the operations allowed on the Availability Service +- Add / Update Availability + - For doctors, admin, other staff members + - Permissions, delegations need to be handled. + - We can consider a delegation framework. +- Get Schedule +- Lists the availability +- Check Availability +- Check availability during a time slot ? +- Schedule + - Request for a schedule + - Helps to handle requests from the public. + - Book a schedule + - Either approve a request or create a new one. +- Approval, Creating an approved schedule to be handled based on permissions. + diff --git a/docs/care/CEP/Draft/0007-scheduling/Design.md b/docs/care/CEP/Draft/0007-scheduling/Design.md deleted file mode 100644 index cb626f1..0000000 --- a/docs/care/CEP/Draft/0007-scheduling/Design.md +++ /dev/null @@ -1,157 +0,0 @@ -# Backend Design - -## Design Thesis - -Each resource - be it doctor, OT, any other facility, including consultation room - will be bookable and have their respective availability charts. - -### Availability and Scheduling -- Make booking and schedule agnostic to business logic. -- Use a factory model design to add specific business logic. - -### Live Queue -- Live Queue will follow a priority score system → a score will be assigned to every booking, depending on multiple factors, and a simple sort on this should allow live queue - -## Models - -### Availability -All resources which can be scheduled shall have an entry into the `SchedulableResource` model. It will be linked back to the original object as well -```py -class SchedulableResource: - id: int - type: enum - "doctor" | "ot" | "lab" | "consultation_room" - - object_id: int # use actual id of the object. -``` - -The availability of a resource will be stored using 3 models - `AvailabilityConfiguration`, `Availability` and `AvailabilityException`. - -The `AvailabilityConfiguration` will be created each time a new configuration is added. When a new configuration is setup, the old one is invalidated by setting the `valid_till` and creating a new configuration. - -```py -class AvailabilityConfiguration: - resource_id: int # foreign key to SchedulableResource - valid_from: datetime - valid_till: datetime - slot_size_in_minutes: int # number of minutes in a slot - tokens_per_slot: int # for OT, Rooms, it will be 1, but for doctors, it can be 10 tokens per 30 minutes, etc. - -class Availability: - configuration_id: int # fk to AvailabilityConfiguration - # can have more than one entry for the same day to support 10-12 and 2-4 - day_of_the_week: int # 0 - 6; Monday is 0 and Sunday is 6. As per Python documentation https://docs.python.org/3/library/datetime.html#datetime.datetime.weekday - start_time: time - end_time - -class AvailabilityException: - resource_id: int - - is_avaiable: bool - # exception can be being available on a non-configured day, - # as well as being not-available on a configured day - - start_datetime - end_datetime - reason # [optional] national holiday, doctor on leave, etc. -``` - -:::warning[Availability as a separate service] -Availability shall be written as a separate service, and all data access should be driven through the service alone. Direct calls to the models should not be allowed. We can achieve this through reviews and try using pre-commit hooks. -::: - -### Booking -```py -class Booking: - patient_id - start_datetime - end_datetime - booked_by - status: enum "requested" | "approved" | "denied" | "canceled" - status_message: str # to be used in case requested bookings were denied, or canceled. - - resources_used: list[BookingResource] # one to many relation with BookingResources - resources_used_json: json # processed json - only for read - -class BookingResource: - booking_id - resource_id - - # separate datetime allows for the provision of some doctors - # being available only for the first few minutes / hours. - start_datetime - end_datetime -``` - -### Dynamic booking vs Slot based booking - -:::info -Generating slots based on availability was one of the first ideas which was discussed. It required a cron job and an asynchronous task to generate slot and handle changes to configuration. - -It was then decided to proceed with a dynamic booking approach as it required less infrastructure and the scale was not identified to be so much. The dynamic mechanism discussed below will use DB row level locking and table based locking mechanism to avoid cross booking. -::: - -**Implementing DB based locking** - -```py -class ScheduleLock: - resource_id: - is_locked: boolean - locked_at: nullable timestamp -``` - -![Scheduling Logic](./scheduling.png) - -:::note -A resource is schedulable if the `is_locked` is `False` OR `is_locked` is `True` but `locked_at` timestamp is older than 1 minute. The 1 minute is the timeframe within which the resource scheduling should be completed by the API. -::: - -#### Sample algorithm for Booking -```py -for resource in resources: - ResourceLock.objects.get_or_create( - resource_id=resource.id, - defaults={ - "is_locked": False, - "locked_at": None, - }, - ) - - try: - with transaction.atomic(): - resources_to_lock = ( - ResourceLock.objects.filter( - resource_id__in=resource_ids_to_lock, - ) - .filter( - Q(is_locked=False) - | Q( - is_locked=True, - locked_at__lt=timezone.now() - timedelta(minutes=1), - ) - ) - .select_for_update(nowait=True, skip_locked=False) - ) - if resources_to_lock.count() != len(resource_ids_to_lock): - raise ValueError("Resource is locked.") - resources_to_lock.update(is_locked=True, locked_at=timezone.now() - ) - except ValueError: - raise Exception(“select_for_update failed”) -``` - -## Operations -Following are the operations allowed on the Availability Service -- Add / Update Availability - - For doctors, admin, other staff members - - Permissions, delegations need to be handled. - - We can consider a delegation framework. -- Get Schedule -- Lists the availability -- Check Availability -- Check availability during a time slot ? -- Schedule - - Request for a schedule - - Helps to handle requests from the public. - - Book a schedule - - Either approve a request or create a new one. -- Approval, Creating an approved schedule to be handled based on permissions. - diff --git a/docs/care/CEP/Draft/0007-scheduling/index.md b/docs/care/CEP/Draft/0007-scheduling/index.md deleted file mode 100644 index a239638..0000000 --- a/docs/care/CEP/Draft/0007-scheduling/index.md +++ /dev/null @@ -1,96 +0,0 @@ -# CEP-7: Scheduling in CARE - -### Motive - -Care is being used in different healthcare environments for various use-cases, including consultation scheduling, OT scheduling, vaccination appointment scheduling, and other appointment management needs. A robust and flexible scheduling system is essential for efficient healthcare delivery and resource management. - -### Requirements - -### 1. Appointment Booking - -- The system shall allow staff members to book appointments for patients. -- Appointments can be scheduled for future dates or as walk-ins for the current day. -- The system shall support different appointment types, including: - - New patient appointments - - Follow-up appointments -- The system shall allow doctors to specify days for specific appointment types (e.g., follow-ups only on certain days). -- Appointments may include multiple participants or be linked to other objects. For example, a procedure could be an object with a team associated with it, and the team would consist of the participants involved in the appointment. -- Future Scope: The system shall provide a public-facing webpage where users can book their own appointments. - -### 2. Resource Management - -- Doctors shall be considered as schedulable resources within the system. -- Doctors shall have the ability to manage their own schedules. -- The system shall allow authorized staff members to manage doctors' schedules on their behalf. -- The system shall support flexible scheduling options, including: - - Fixed slot durations (e.g., 15-minute intervals) - - Daily patient limits (e.g., maximum of 20 patients per day) - -### 3. Availability Management - -- The system shall have the capability to show availability of resources (doctors, rooms, equipment, etc.). -- Doctors shall be able to set their available days and times. -- Doctors shall have the option to mark certain periods as unavailable. -- Doctors shall be able to specify which days in the future they can be booked. -- The system shall respect all availability settings when allowing appointments to be booked. -- The system shall support recurring availability patterns (e.g., available every Monday and Wednesday, 9 AM to 5 PM). -- Authorized staff members shall be able to view and modify availability settings on behalf of doctors, subject to appropriate permissions. - -### 4. Check-in and Encounter Management - -- The system shall provide a check-in function for when patients arrive at the facility. -- An encounter shall only be created in the system upon patient check-in. -- The system shall automatically manage a token system for patient queuing. (mix of appointments + walk-ins) [Logic is an open challenge to be tackled with Roopak] -- The system shall implement a check-in based priority system that automatically manages appointments and walk-ins. -- The check-in process shall update the patient's queue position based on their appointment time and arrival time. - -### 5. Priority Management - -- The system shall support different levels of patient priority. -- Staff shall have the ability to prioritize a VIP patient in the queue. -- The system shall provide clear visibility of the current queue order to staff members. -- The priority management system shall be flexible enough to accommodate emergency situations. - -### 6. Appointment Statuses - -- The system shall support multiple statuses for scheduled visits, including but not limited to: - - Scheduled - - Checked In - - In Progress - - Completed - - Cancelled - - No Show -- The system shall allow for status updates throughout the appointment lifecycle. - -### 7. Location-based Scheduling - -- The system shall support the registration and booking of various locations, including but not limited to Operating Theaters. -- This implementation shall be generic, allowing extension to support other types of location-based scheduling (e.g., vaccination appointment scheduling). -- Users with appropriate access rights shall be able to book locations. -- Location schedules shall be manageable by designated managers (can be the owner of the location schedule). -- The system shall support the handling of emergency cases that may affect location schedules. - -### 8. User Permissions - -- The system shall implement role-based access control for scheduling functions. -- Specific permissions shall be required for managing doctors' schedules on their behalf. -- Location booking shall be restricted to authorized doctors and staff members. - -### 9. Flexibility and Customization - -- The system shall allow for customization of appointment durations based on doctor or appointment type preferences. -- The system shall support different scheduling rules for different departments or specialties. - -### 10. Notifications - -- The system shall send notifications to relevant staff members for schedule changes, new bookings, and cancellations. -- Future: Allow SMS and whatsapp confirmations for patients - -### 11. Audit Trail - -- The system shall maintain a comprehensive audit trail of all scheduling actions, including creations, modifications, and cancellations. -- It shall support comments that can be added by users with access to the object. - -### 12. Future Beckn Integration - -- The architecture shall be flexible enough to accommodate Beckn protocols and standards for interoperable scheduling. diff --git a/docs/care/CEP/Draft/0007-scheduling/scheduling.png b/docs/care/CEP/Draft/0007-scheduling/scheduling.png deleted file mode 100644 index fb525ebd284218bce51fa74f8f0f6c1a9d1e52c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21264 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 E0KmUeNdN!< From c14b3f56e37bd57c2ecac7c510d7ebc98dfb0eaa Mon Sep 17 00:00:00 2001 From: Roopak A N Date: Thu, 17 Oct 2024 15:32:49 +0530 Subject: [PATCH 3/4] rewrite models with one example --- docs/care/CEP/Draft/0007-scheduling.md | 239 ++++++++++++++++++------- 1 file changed, 172 insertions(+), 67 deletions(-) diff --git a/docs/care/CEP/Draft/0007-scheduling.md b/docs/care/CEP/Draft/0007-scheduling.md index eaaa10b..d0c7e0c 100644 --- a/docs/care/CEP/Draft/0007-scheduling.md +++ b/docs/care/CEP/Draft/0007-scheduling.md @@ -113,75 +113,79 @@ Each resource - be it doctor, OT, any other facility, including consultation roo ### Availability All resources which can be scheduled shall have an entry into the `SchedulableResource` model. It will be linked back to the original object as well -```py -class SchedulableResource: - id: int - type: enum - "doctor" | "ot" | "lab" | "consultation_room" - - object_id: int # use actual id of the object. -``` The availability of a resource will be stored using 3 models - `Schedule`, `Availability` and `AvailabilityException`. The `Schedule` will be created each time a new configuration is added. When a new configuration is setup, the old one is invalidated by setting the `valid_till` and creating a new configuration. -```py -class Schedule: - resource_id: int # foreign key to SchedulableResource - valid_from: datetime - valid_till: datetime - slot_size_in_minutes: int # number of minutes in a slot - tokens_per_slot: int # for OT, Rooms, it will be 1, but for doctors, it can be 10 tokens per 30 minutes, etc. - -class Availability: - schedule_id: int # fk to Schedule - # can have more than one entry for the same day to support 10-12 and 2-4 - day_of_the_week: int # 0 - 6; Monday is 0 and Sunday is 6. As per Python documentation https://docs.python.org/3/library/datetime.html#datetime.datetime.weekday - start_time: time - end_time - -class AvailabilityException: - resource_id: int - - is_avaiable: bool - # exception can be being available on a non-configured day, - # as well as being not-available on a configured day - - start_datetime - end_datetime - reason # [optional] national holiday, doctor on leave, etc. +```mermaid +erDiagram + Schedule }|--|{ Schedulable-Resource : "of a" + Schedule ||--|{ Availability: contains + Schedulable-Resource ||--|{ AvailabilityException: contains + + Schedule { + int resource_id + datetime valid_from + datetime valid_till + } + + Availability { + enum type "tokens / open" + int slot_size_in_minutes "0 for open" + int tokens_per_slot "0 for open" + int day_of_the_week "# 0 - 6; Monday is 0 and Sunday is 6. As per Python docs" + datetime start_time + datetime end_time + } + + AvailabilityException { + enum type "tokens / open" + bool is_available "allows to be absent in a configuration, or be available outside a configuration" + datetime start_datetime + datetime end_datetime + } + ``` + + + + :::warning[Availability as a separate service] Availability shall be written as a separate service, and all data access should be driven through the service alone. Direct calls to the models should not be allowed. We can achieve this through reviews and try using pre-commit hooks. ::: ### Booking -```py -class Booking: - patient_id - start_datetime - end_datetime - booked_by - status: enum "requested" | "approved" | "denied" | "canceled" - status_message: str # to be used in case requested bookings were denied, or canceled. - - # resources_used: list[BookingResource] # one to many relation with BookingResource - -class BookingResource: - booking_id - resource_id - - # separate datetime allows for the provision of some doctors - # being available only for the first few minutes / hours. - start_datetime - end_datetime -``` :::warning DB level constraints to avoid overbooking DB level constraint shall be added to `BookingResource` table, which will check if the resource is being used in another `Booking` ::: +```mermaid +erDiagram + Staff ||--o{ Booking: books + Patient ||--o{ Booking: "for a" + Booking ||--|{ BookingResource: "contains" + Booking ||--o| Availability: "part of" + Booking ||--o| AvailabilityException: "part of" + + Booking { + int patient FK + int booked_by FK + enum status "requested | approved | denied | canceled" + datetime start_datetime + datetime end_datetime + } + + BookingResource { + int resource FK + datetime start_datetime "allows for some resources being partially available during a booking" + datetime end_datetime + } + +``` + ### Dynamic booking vs Slot based booking :::info @@ -216,23 +220,31 @@ flowchart TD ### Token Booking Token booking and Booking needs to be handled seperately as Booking of resources needs to override the tokens booked. -```py -class TokenSlot: - resource_id - tokens_remaining - start_datetime - end_datetime - -class TokenBooking: - slot_id # FK to Slot - patient_id - start_datetime - end_datetime - booked_by - status: enum "requested" | "approved" | "denied" | "canceled" - status_message: str # to be used in case requested bookings were denied, or canceled. +```mermaid +erDiagram + TokenSlot }o--o| Availability: "can be part of a" + TokenSlot }o--o| AvailabilityException: "can be part of a" + Patient ||--o{ TokenBooking: "can book" + TokenSlot ||--o{ TokenBooking: "has multiple" + + TokenSlot { + int availability FK "nullable; created for availability type token" + int availabilty_exception FK "nullable; created for availability type token" + datetime start_datetime + datetime end_datetime + int tokens_remaining + } + + TokenBooking { + int patient FK + int token_slot FK + int booked_by FK + enum status "requested | approved | denied | canceled" + } + ``` + **Sample Algorithm** ```mermaid --- @@ -266,3 +278,96 @@ Following are the operations allowed on the Availability Service - Either approve a request or create a new one. - Approval, Creating an approved schedule to be handled based on permissions. + +## Example + +:::info Example +Dr. Roopak is available in weekdays Mon-Fri +in the hospital from 10 am to 5 pm, and a lunch break from 1 pm to 2 pm. +He is open for OP during 10 am to 12 pm. +On Oct 25, Friday he is unavailable. He will compensate for that on Oct 28. +::: + +```py +Schedule: + resource: Doctor Roopak + valid_from: Oct 1, 2024 + valid_till: Oct 31, 2024 + +Availability / 1 + type: "tokens" + slot_size_in_minutes: 30 + tokens_per_slot: 10 + day_of_the_week: Mon + start_time: 10 am + end_time: 12 pm + +Availability / 2 + type: "open" + slot_size_in_minutes: None + tokens_per_slot: None + day_of_the_week: Mon + start_time: 12 pm + end_time: 1 pm + +Availability / 3 + type: "open" + day_of_the_week: Mon + start_time: 2 pm + end_time: 5 pm + +AvailabilityException / 1 + type: "open" + is_available: false + start_datetime: Oct 25, 2024 10 am + end_datetime: Oct 26, 2024 9 pm + +AvailabilityException / 2 + type: "token" + is_available: true + start_datetime: Oct 28, 2024 10 am + end_datetime: Oct 28, 2024 12 pm + +AvailabilityException / 3 + type: "open" + is_available: true + start_datetime: Oct 28, 2024 2 am + end_datetime: Oct 28, 2024 5 pm +``` + +**He is operating on a patient on Oct 11 2 pm to 4 pm.** + +```py +Booking: + patient + start_datetime: Oct 11 2 pm + end_datetime: Oct 11 4 pm + booked_by: Staff ID + status: approved + +BookingResource: + resource: Dr. Roopak + start_datetime: Oct 11 2 pm + end_datetime: Oct 11 4 pm +``` + +**2 people need tokens for his OP on 20 Oct 10 - 10:30** +``` +TokenSlot + availabilty_id + start_datetime 20 Oct 10 am + end_datetime 20 Oct 10:30 am + tokens_remaining 8 + +TokenBooking + patient: Patient 1 + token_slot_id + booked_by: FK to Object - Patient, Staff + status: approved + +TokenBooking + patient: Patient 2 + token_slot_id + booked_by: FK to Object - Patient, Staff + status: approved +``` From 9c1f121aac901bf00b06b78f42f6e030e82fd464 Mon Sep 17 00:00:00 2001 From: Roopak A N Date: Tue, 29 Oct 2024 16:24:37 +0530 Subject: [PATCH 4/4] fix review comments --- docs/care/CEP/Draft/0007-scheduling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/care/CEP/Draft/0007-scheduling.md b/docs/care/CEP/Draft/0007-scheduling.md index d0c7e0c..1d7e729 100644 --- a/docs/care/CEP/Draft/0007-scheduling.md +++ b/docs/care/CEP/Draft/0007-scheduling.md @@ -207,7 +207,7 @@ title: Scheduling logic flowchart TD start(((Start))) --> sReq["Schedule Request"] sReq --> vReqdata["Validate Basic Request Data"] - vReqdata --> vAvailability["Validate Basic Request Data"] + vReqdata --> vAvailability["Validate Availability"] vAvailability --> lockCheck{"Can acquire all locks?"} lockCheck --> |Yes| sc["Schedule"] sc --> rel["Release all acquired locks"]