diff --git a/nsdk/explanations/agenda/big_overbooking.svg b/nsdk/explanations/agenda/big_overbooking.svg new file mode 100644 index 0000000..98b2411 --- /dev/null +++ b/nsdk/explanations/agenda/big_overbooking.svg @@ -0,0 +1,4 @@ + + + +
3
4
2
1
6
Overbooking
1 availability
1 availability
5
Out of working hours
Out of working hours
\ No newline at end of file diff --git a/nsdk/explanations/agenda/calendar_items_groups.svg b/nsdk/explanations/agenda/calendar_items_groups.svg new file mode 100644 index 0000000..55ccd0c --- /dev/null +++ b/nsdk/explanations/agenda/calendar_items_groups.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/nsdk/explanations/agenda/index.mdx b/nsdk/explanations/agenda/index.mdx new file mode 100644 index 0000000..17bfd22 --- /dev/null +++ b/nsdk/explanations/agenda/index.mdx @@ -0,0 +1,416 @@ +--- +sidebar_position: 50 +--- + +# Agenda system + +Cardinal’s agenda system lets you schedule and manage user appointments. + +The core entities are `Agenda` and `CalendarItem`. + +- **CalendarItem**: Represents an event or appointment scheduled at a specific time within an agenda. +- **Agenda**: A container for calendar items, which can include basic metadata such as a name and an owner. + +For example, a doctor might have an agenda to track patient appointments by adding calendar items. +If your application involves only a small, trusted group of users, creating calendar items by manipulating directly +these models may be sufficient. + +However, if you need to allow less-trusted users to book appointments, you can use **availabilities discovery** and +**safe booking** features. +By defining a schedule for an agenda, you enable any user to: +1. Query available time slots. +2. Book appointments that align with those availabilities. + +This page explains how to model the agenda schedule in ways that can best represent your use cases. + +## Basics + +### Schedules model + +The core element for defining an agenda schedule is the `EmbeddedTimeTableItem`. + +An `EmbeddedTimeTableItem` defines part of a schedule by specifying: +- One or more start–end hour pairs that define the hours when the timetable item has availability. +- An [iCalendar recurrence rule](https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.10) that define the days when +- the timetable item has availability. +- A set of `CalendarItemType` IDs that indicate which types of appointments can be booked during those times. +- The number of availabilities for appointments that happen at the same time. + +If a schedule has different hours on different days, or serves different types of appointments at different times, you +will need to use multiple timetable items to build the schedule. + +Every timetable item is also implicitly tied to a **resource group**. +A resource group is the group of people (usually healthcare parties) or other resources (such as medical devices) that +can handle appointments with times and types matching what is defined by the timetable item. +Typically, the number of parallel availabilities for a timetable item should match the number of resources in the +associated group. +Resource groups and their effect on availabilities are explained in more detail in a later section. + +:::note + +There is no entity in cardinal that represents resource groups; it is instead only a concept used in the design of agendas' +schedules, and calculation of availabilities. + +::: + +Timetable items for the same resource group are collected into `ResourceGroupAllocationSchedule`s which are then +embedded in `Agenda.schedules`. + +A `ResourceGroupAllocationSchedule` can also specify a period of time when it is active. +This way `ResourceGroupAllocationSchedule` a serves two purposes: +- Group timetable items that use the same resource group +- Divide timetable items that have different periods of validity. This is useful if you want to represent seasonal + changes to the schedule, or in general upcoming significant changes to the schedule. + +For example, assuming today is April 2025, if you have an agenda that has the schedule for two groups (group1, group2) +and the schedule for both groups is going to change on the 1st December 2025, you will have four +`ResourceGroupAllocationSchedule`: +- A schedule for group 1 valid until the 1st December 2025 (excluded) +- A schedule for group 2 valid until the 1st December 2025 (excluded) +- A schedule for group 1 valid since the 1st December 2025 (included) +- A schedule for group 2 valid since the 1st December 2025 (included) + +:::info + +When you have multiple resource groups in an agenda you should specify explicitly the resource group for each +`ResourceGroupAllocationSchedule`. You can do this by using codes to identify different resource groups, and +setting the corresponding `CodeStub` in `ResourceGroupAllocationSchedule.resourceGroup`. + +::: + +:::info + +If in an agenda there are multiple entries of `ResourceGroupAllocationSchedule` for the same resource group the entries +must be active at different and non-overlapping times. + +::: + +:::info + +A single agenda's schedule can have `ResourceGroupAllocationSchedule` for different resource groups, but normally a +resource group should appear in only one agenda. + +::: + +Finally, an agenda schedule must define how available time frames should be converted into bookable slots, using +`Agenda.slottingAlgorithm`. +The slotting algorithm ensures that appointments are booked at regular times rather than at arbitrary offsets. + +For example, if doctor visits last 20 minutes and working hours are 09:00–12:00, you wouldn’t want a booking from +09:15–09:35, as it would leave no room before 09:15. +With an appropriate slotting algorithm, users could only book in 20-minute intervals starting at 09:00 (e.g., 09:00, +09:20, 09:40, …). + +:::info + +Currently, the only supported slotting algorithm generates slots at fixed intervals of a configurable length. +However, this may not always be ideal for agendas where appointments can have different durations. + +We plan to develop additional slotting algorithms to allow covering more use cases. +If you have specific needs, please contact us to share your use case. + +::: + +### Appointments + +Appointments are represented by calendar items. + +When a user wants to take an appointment they have to choose an agenda, query the availabilities for the type of +appointment they want, and finally create a calendar item linked to the agenda, and at the time of the availability. +*Note that in this case the calendar item is linked only to the agenda*. + +:::note + +Calendar items are never linked directly to timetable items or resource group allocation schedules. + +Users with special privileges can link calendar items to certain resource groups in addition to the agenda, but this +should be done only under special circumstances, which will be explained in later sections; most calendar items should +be linked only to agendas. + +::: + +## Agenda schedule design recommendations + +When designing agenda schedules, the first question to consider is what choices you want to leave to the end users. + +For example, in a system where patients can book doctor visits, the schedule design determines whether users can: +- Select a specific doctor for their appointment, or +- Select only the type of appointment, while the system (or another process) assigns the doctor at a later moment. + +The design of agendas for the first kind of system is straightforward: you have an agenda for each doctor, and each +agenda uses only one resource group in the schedule, that is the group consisting of only that doctor. + +To design the agendas for the second type of system you will have to first divide your doctors in resource groups, based +on their competences (i.e. the types of appointments that they can handle) and working hours. +Doctors with the same exact set of competences and working hours should be part of the same group. + +:::warning + +We strongly recommend you follow the above rules in the design of resource groups, otherwise you may get unexpected +results when querying agendas availabilities, especially in the presence of irregular appointments (discussed later). + +::: + +You should then divide the groups in agendas: if two groups can serve the same type of calendar item at overlapping +times you can put them in the same agenda. +This allows for significant flexibility in the availabilities, as the appointments are not assigned to a specific group +and can *slide* depending on the needs of other appointments. + +
+Example + +Consider an agenda with the following: +- A schedule for Group 1, with one availability on Monday between 09:00 and 12:00 for calendar items of type `t0` or `t1`. +- A schedule for Group 2, with one availability on Monday between 10:00 and 13:00 for calendar items of type `t0` or `t2`. + +If there is an appointment of type `t1` between 10:00 and 10:30 then the at that time there is only one availability for +type `t0` or `t2`. + +However, if the appointment is of type `t0` then the availability will be for any of types `t0`, `t1`, or `t2`; this is +because the existing appointment is not linked to group 1 or group 2, and could be performed by either of them. +If there is a new appointment between 10:00 and 10:30 of type `t1` then the original `t0` appointment will be performed +by group 2; if the new appointment is instead of type `t2` the original appointment will be performed by group 1. + +This kind of flexibility is not possible if group 1 and group 2 are on separate agendas; the group that will take care +of the `t0` appointment is chosen at the moment the calendar item is created, since the calendar item will be linked +directly to the agenda of one of the groups. + +
+ +If you don't have any overlaps between groups, or don't care for this kind of flexibility, you will have an agenda per +group, which has no significant differences from the design where the end-user picks the doctor. + +:::warning + +You should include multiple resource groups in the same agenda only if their schedules overlap in a way that allows +flexible assignment of certain calendar items. + +Adding unrelated resource groups to the same agenda provides no benefit. +On the contrary, it increases the complexity of availability calculations. +The more complex the agenda, the slower availability queries will be, and, in extreme cases, some availabilities may not +be found at all. + +::: + +:::info + +You can combine these design with frontend logic to create more complex systems. + +For example, you can create a system where the user can choose between searching for availabilities of specific doctors +or searching for available doctors for the type of appointment. + +On the side of cardinal this would use one agenda per doctor, and this would work out-of-the-box for the search of +availabilities for specific doctors. + +The search for available doctors for the type of appointment instead would do multiple queries: first the client +will search for agendas matching the requirements (based on the type of appointments served, but usually also based on +the location), then query the availabilities for the individual agendas. + +::: + +## Irregular appointments + +Most appointments in an agenda will follow its schedule. +However, some may not fit within it exactly; these are called **irregular appointments**. + +There are two types of irregular appointments: +- **Off-schedule appointments**: Calendar items that either fall (partly or fully) outside the scheduled times, or use + a `CalendarItemType` that is not permitted at that time. +- **Overbookings**: Occur when multiple appointments individually fit the schedule, but together exceed its capacity. + +Irregular appointments can occur in two situations: +- A privileged user creates an appointment directly, bypassing the safe booking process. +- The schedule is modified after appointments have already been booked. + +Even when irregular appointments exist, Cardinal will still attempt to provide reasonable availability results. +However, there are a few considerations you can follow to achieve the best outcomes. + +### Availabilities algorithm overview + +This section explains how Cardinal calculates availabilities for an agenda, which will help you understand how to handle +irregular appointments. + +In general, an appointment is considered available in an agenda if: +- At least one timetable item in the agenda can serve it (both in type and time), and +- Adding the appointment does not create conflicts with other appointments already booked in that agenda. + +To check for conflicts, Cardinal simulates an assignment process: each existing calendar item is "assigned" to timetable +items in the agenda. +If the algorithm can assign all existing calendar items in a way that leaves a timetable item with free capacity during +a certain time frame, then that time frame is considered available: creating a calendar item during that time won't +cause conflicts. + +Normally, the assignment algorithm can assign a calendar item to a timetable item only if: +- The timetable item can fully accommodate the appointment (both in type and time), and +- The timetable item still has available capacity for that appointment, after considering the capacity used to serve + the other appointments that were already assigned to it. + +These rules make up what we call the **strict** assignment strategy; however, in some cases the algorithm uses the +**loose** assignment strategy. +The choice between strict and loose is made individually for each calendar item; by default the strict strategy is used, +but the algorithm uses the loose strategy if: +- The calendar item is an off-schedule appointment and does not explicitly request strict assignment, or +- The calendar item explicitly requests loose assignment. + +:::info + +Privileged users can instruct the availability algorithm to use a specific assignment strategy for certain calendar +items by setting the corresponding value in the `CalendarItem.availabilitiesAssignmentStrategy` field. + +::: + +A calendar item can be **loose**ly assigned to a timetable item if: +- Its time at least partially overlaps with the timetable item’s hours, and +- The timetable item’s resource group is eligible for the assignment, which is true if: + - The calendar item is explicitly linked to that resource group, or + - The calendar item is not explicitly linked to any resource group, but the resource group is considered a suitable + match based on internal heuristics. + +:::note + +Loose assignment doesn't require that the timetable item has enough remaining capacity for the calendar item, however, +the loose assignment will still affect the remaining capacity for strict assignment. + +::: + +:::info + +The heuristics used when deciding if a resource group is suitable for loose assignment of calendar item consider +information such as: +- Does the resource group have a competence for the calendar item type (i.e. is there a timetable item for that + resource group that can serve that calendar item type). +- Does the resource group have a timetable item that can fully serve the calendar item times, even if it doesn't serve + the calendar item type during that time. +- Does the resource group have a timetable item that can partially serve the calendar item times, and serves that + calendar item type during that time. + +This is one reason why it is important to model resource groups in the appropriate way. + +The exact heuristics and priorities used are an implementation detail that may change without notice; this is why +you should always specify explicitly the resource group when possible, as the availabilities detected may otherwise +change between different versions of the cardinal backend. + +::: + +:::warning + +Off-schedule calendar items that have no overlap with any timetable item are ignored by the availabilities algorithm. + +::: + + +### Off-schedule appointments + +Off-schedule appointments unless otherwise specified are always assigned using loose assignment when searching for +availabilities, which is a middle ground between ignoring the inconsistencies and blocking completely the agenda. + +However, loose assignment can sometimes be too permissive, especially with calendar items that are not explicitly linked +to a resource group. + +For example, given an agenda with the following groups: +- Group 1 from 09:00 to 12:00 1 availability for calendar item type 0 or 1 +- Group 2 from 09:00 to 12:00 1 availability for calendar item type 0 or 2 + +If you have two off-schedule calendar items from 11:30 to 12:30 of type 0 with no resource group specified explicitly, +they could both be assigned to the same group: this is because when a calendar item is assigned loosely the potential +conflicts with other calendar items are not checked. +This means there will still be availabilities for type 0, 1, or 2, for the entire day (09:00–12:00). + +Linking one of the calendar items to group 1 and the other to group 2 instead would change the availabilities to +09:00–11:30, as you would expect. + +When creating new off-schedule appointments, or when changing a schedule in a way that may turn existing regular +appointments into off-schedule, you should always make sure to set an appropriate resource group on the calendar item. + +### Overbooking + +In case of overbooking it will always be impossible to assign all calendar items of an agenda in the search of +availabilities; this would be a major issue, as even a short overbooking would completely block an agenda. + +However, what actually happens in Cardinal is that in case of overbooking only the times covered by the overbooking +calendar items and calendar items whose assignment depends on the overbooking calendar items will be blocked. + +This can minimize the impact that overbooking has on availability. +For example, the following diagram shows a potential overbooking situation, in an agenda that has 2 availabilities. + +![Example diagram of overbooking](./overbooking_example.svg) + +Since there are only 2 availabilities but at a certain time there are 3 calendar items overlapping (`2`, `3`, and `4`) +we have an overbooking. + +Even though there would be an availability during the second half of calendar item `5` the overbooking blocks that +availability; however since calendar items `1` and `6` are independent from the group of calendar items in overbooking +(`2-5`) the availabilities at that time are unaffected. + +This situation is particularly favorable as the overbooking could be solved by moving the calendar item `4` to match the +same times as calendar item `5`, and even if this is not done users will still be able to take appointments at +appropriate times. + +In some cases however, this system may be too permissive. +Considering again an agenda with 2 availabilities and the following calendar items: + +![Example diagram of overbooking 2](./big_overbooking.svg) + +In this case the overbooking could be solved by moving one of the calendar items in the overbooked group to the same +times of calendar item `1`, and one of the calendar items to the same times of `6`. +Until you do this change, however, people can still book appointments; if one more appointment is booked before you +fix the overbooking it will be impossible to resolve it. + +#### Dependent calendar items assignment clarifications + +Two calendar items are considered **dependent** if assigning one of them to a timetable item can affect how the other +is assigned, but only calendar items that use strict assignment strategy are considered when checking dependencies. + +A calendar item is dependent to: +- All calendar items (for the same agenda) that have scheduled time overlapping with it. For the sake of this + we call this a direct dependency, but this has no impact on the availabilities. +- All calendar items that are dependent directly or indirectly with one of its dependencies (indirect dependency). + +Note that two tangent calendar items (i.e. the start of one is equal to the end of the other) are not directly +dependent. + +The following diagram shows different groups of dependent calendar items in different colors. + +![Diagram of dependent calendar items](./calendar_items_groups.svg) + +#### Blocking more slots + +In situations where the system is too permissive, you can create fake calendar items to block people from booking +calendar items in a larger period than what the overbooking normally affects. + +The simplest option is to create a calendar item with `strict` assignment strategy, spanning the times you want to block +(and including the times of the overbooking). + +This will essentially artificially increase the group of calendar-items that are dependent to the calendar items in +overbooking, which in turn increases the period of time that will be blocked by it. + +## Temporary changes in availabilities + +Sometimes you'll have to represent temporary changes in the availabilities, for example, if a healthcare party is on +leave. + +The preferred way of doing this is to create calendar items matching the period of the unavailability. +If the unavailable resource is part of an agenda with multiple resource groups, you should link the calendar item to the +resource group. +You don't have to match exactly the start/end hours of the schedule, you can use an off-schedule calendar item in this +case. + +:::warning + +You should avoid creating calendar items lasting longer than 1 year; these calendar items will be ignored when +calculating availabilities. + +::: + +:::note + +Reducing availabilities can cause existing appointments to turn into overbookings. + +To avoid this, you may configure the agenda to ignore availabilities that are too far into the future by using +`EmbeddedTimeTableItem.notBeforeInMinutes`. +For example, if a healthcare party schedules leave with enough notice, this setting ensures that no conflicting +appointments (created by unprivileged users) will exist. + +::: + +For significant and long-lasting changes to the schedule, it is generally better to modify the agenda instead. diff --git a/nsdk/explanations/agenda/overbooking_example.svg b/nsdk/explanations/agenda/overbooking_example.svg new file mode 100644 index 0000000..b8c2798 --- /dev/null +++ b/nsdk/explanations/agenda/overbooking_example.svg @@ -0,0 +1,4 @@ + + + +
3
4
2
1
6
Area affected by overbooking
0 availabilities
1 availability
1 availability
Availabilities here are actually exhausted
Here there should be 1 availability, but overbooking blocks it
5
\ No newline at end of file diff --git a/nsdk/explanations/data-model/agenda.mdx b/nsdk/explanations/data-model/agenda.mdx index b5d530e..3a13809 100644 --- a/nsdk/explanations/data-model/agenda.mdx +++ b/nsdk/explanations/data-model/agenda.mdx @@ -1,29 +1,7 @@ -# Agenda - -The Agenda is a non-encryptable root-level entity that represents an agenda for a user. - -## Properties - -Below you will find an explanation of the most commonly used properties in the entity that are not among the -[shared fields](/explanations/data-model/#shared-fields). For a full list, check the reference documentation (:construction:). - -### closingDate -The `closingDate` represent the moment when the Agenda ended. -It is encoded as a [FuzzyDateTime](/explanations/data-model/#fuzzydatetime). +--- +sidebar_position: 0 +--- -### name -A human-readable description for the Agenda. - -### userId -An optional link to a user that encodes the fact that the Agenda is related to a specific user. - -### rights -A collection of Rights that define the rights of the user on the Agenda. - -A Right is a nested entity that contains the following properties: - -- `userId`: the id of the user that has the right. -- `read`: if the user can read the agenda content -- `write`: if the user can write in the agenda -- `administration`: if the user can administer the agenda +# Agenda +:construction: \ No newline at end of file diff --git a/nsdk/explanations/data-model/calendaritem.mdx b/nsdk/explanations/data-model/calendaritem.mdx index 8dc7454..7fc28bf 100644 --- a/nsdk/explanations/data-model/calendaritem.mdx +++ b/nsdk/explanations/data-model/calendaritem.mdx @@ -1,5 +1,15 @@ +--- +sidebar_position: 0 +--- + # Calendar item +:::info + +:construction: Part of this page is still under construction + +::: + The CalendarItem is an encryptable root-level entity that records the an appointment for a the patient. ## Fields Encrypted by Default diff --git a/nsdk/explanations/data-model/code.mdx b/nsdk/explanations/data-model/code.mdx index 33426a8..65e0b52 100644 --- a/nsdk/explanations/data-model/code.mdx +++ b/nsdk/explanations/data-model/code.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Code A Code is a non-encryptable root-level entity that represents a concept from a medical codification system (e.g., diff --git a/nsdk/explanations/data-model/codestub.mdx b/nsdk/explanations/data-model/codestub.mdx index 141f24b..1af3a34 100644 --- a/nsdk/explanations/data-model/codestub.mdx +++ b/nsdk/explanations/data-model/codestub.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # CodeStub A CodeStub is a non-encryptable nested entity that represent a reference to a [Code](/explanations/data-model/code). diff --git a/nsdk/explanations/data-model/contact.mdx b/nsdk/explanations/data-model/contact.mdx index 070bbac..df63352 100644 --- a/nsdk/explanations/data-model/contact.mdx +++ b/nsdk/explanations/data-model/contact.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Contact The Contact is an encryptable root-level entity that records the medical information about the patient. A visit to the patient's house, a diff --git a/nsdk/explanations/data-model/content.mdx b/nsdk/explanations/data-model/content.mdx index 26aabf7..9ab64fc 100644 --- a/nsdk/explanations/data-model/content.mdx +++ b/nsdk/explanations/data-model/content.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Content A Content is an encryptable nested entity that contains the value of the medical data to store in the cloud. It is nested diff --git a/nsdk/explanations/data-model/device.mdx b/nsdk/explanations/data-model/device.mdx index a1624eb..ea61487 100644 --- a/nsdk/explanations/data-model/device.mdx +++ b/nsdk/explanations/data-model/device.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Device A Device is a non-encryptable root-level entity that represents any medical device. diff --git a/nsdk/explanations/data-model/document.mdx b/nsdk/explanations/data-model/document.mdx index d5cf8e1..79ae682 100644 --- a/nsdk/explanations/data-model/document.mdx +++ b/nsdk/explanations/data-model/document.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Document A Document is an encryptable root-level entity that represents any kind of medical document, from reports to medical imaging diff --git a/nsdk/explanations/data-model/fuzzy_date_time.mdx b/nsdk/explanations/data-model/fuzzy_date_time.mdx new file mode 100644 index 0000000..8dc8468 --- /dev/null +++ b/nsdk/explanations/data-model/fuzzy_date_time.mdx @@ -0,0 +1,62 @@ +--- +sidebar_position: 100 +--- + +# Fuzzy Dates and Times + +Some entities in iCure use as data types for time information the types `FuzzyDate`, `FuzzyDateTime`, or `FuzzyTime`. + +:construction: Currently these types do not have a proper representation as classes in the Cardinal SDK, and are instead +represented by simple numbers: +- 64-bit integers are used for `FuzzyDateTime` in languages that support it, 64-bit floating point otherwise. +- 32-bit integers are used for `FuzzyDate` and `FuzzyTime` in languages that support it, 64-bit integer or 64-bit + floating point otherwise. + +In the future we are planning to provide for each language of the SDK proper classes to represent these data types and +to allow interoperability with each language's standard time libraries. + +## Representation + +All fuzzy date/time values are considered local, therefore, they don't contain information about the time zone. + +A fuzzy date is represented by an 8 digits number: +- The first 4 digits represent the (gregorian calendar) year +- The next 2 digits represent the month (1 being January, 12 December) +- The last 2 digits represent the day (starting from 1) + +A fuzzy time is represented by a 6 digits number: +- The first 2 digits represent the hours (0–23) +- The next 2 digits represent the minutes (0–59) +- The last 2 digits represent the seconds (0–59) + +A fuzzy date time is the concatenation of a fuzzy date and fuzzy time. + +## Encoding the precision + +Normally `FuzzyDate`s are precise to the day, while `FuzzyDateTime`s and `FuzzyTime`s are precise to the second. + +However, when the exact time for something such as an health element's value date is not known or not relevant, you can +use special values to indicate that the fuzzy date/time is using a different precision: +- For year precision the year is set and all other digits are 0 (e.g. 20250000000000 is a `FuzzyDateTime` representing the year 2025 and unknown month/day/time) +- For month precision the year and month are set, and all other digits are 0 (e.g. 20250100 is a `FuzzyDate` representing the January 2025 and an unknown day) +- For day precision (on `FuzzyDateTime`) the day is set to one day before the actual date, and the remaining digits are set to 240000 (e.g. 20250131240000 represents Febraury 1st 2025, at unknown time) +- For hour precision the hour is set to one before the actual hour, and the remaining digits are set to 6000 (e.g. 20250131236000 represents Febraury 1st 2025 during the first hour of the day but unknown minute and seconds) +- For minute precision the minute is set to one before the actual minute, and the seconds are set to 60 (e.g. 162960 represents 16:30 at unknown seconds) + +:::note + +Fuzzy dates and times with precision information are not always acceptable values. + +For some entities such as agendas' schedules and calendar items times the fuzzy dates and times must use the full +precision. + +::: + +:::note + +Combinations of the special values are not allowed; for example you can't have a `FuzzyDateTime` with unknown month and day but known year and time. + +::: + + + diff --git a/nsdk/explanations/data-model/healthcareparty.mdx b/nsdk/explanations/data-model/healthcareparty.mdx index 5facbf0..c487265 100644 --- a/nsdk/explanations/data-model/healthcareparty.mdx +++ b/nsdk/explanations/data-model/healthcareparty.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # HealthcareParty A HealthcareParty is a non-encrypted, root-level entity that represents any actor involved in the care of the patient. diff --git a/nsdk/explanations/data-model/healthelement.mdx b/nsdk/explanations/data-model/healthelement.mdx index e57b402..171fbd1 100644 --- a/nsdk/explanations/data-model/healthelement.mdx +++ b/nsdk/explanations/data-model/healthelement.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # HealthElement A HealthElement is an encryptable, root-level entity that represents a medical event persistent in time. For example, diff --git a/nsdk/explanations/data-model/identfier.mdx b/nsdk/explanations/data-model/identfier.mdx index 6de3823..2d6e455 100644 --- a/nsdk/explanations/data-model/identfier.mdx +++ b/nsdk/explanations/data-model/identfier.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Identifier An Identifier is a non-encryptable nested entity that is used in the `identifiers` field of other entities to diff --git a/nsdk/explanations/data-model/index.mdx b/nsdk/explanations/data-model/index.mdx index f471e29..0041ec4 100644 --- a/nsdk/explanations/data-model/index.mdx +++ b/nsdk/explanations/data-model/index.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 10 +--- + import {LanguageTabs} from "@site/src/components/LanguageTabs"; import TabItem from "@theme/TabItem"; diff --git a/nsdk/explanations/data-model/message.mdx b/nsdk/explanations/data-model/message.mdx index 16890a8..cca7058 100644 --- a/nsdk/explanations/data-model/message.mdx +++ b/nsdk/explanations/data-model/message.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Message A Message is an encryptable, root-level entity that can be use to implement an encrypted messaging systems between users diff --git a/nsdk/explanations/data-model/patient.mdx b/nsdk/explanations/data-model/patient.mdx index 4d1a562..c714da2 100644 --- a/nsdk/explanations/data-model/patient.mdx +++ b/nsdk/explanations/data-model/patient.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Patient A Patient is an encryptable, root-level entities that represents a patient, the subject of treatment or medical data diff --git a/nsdk/explanations/data-model/service.mdx b/nsdk/explanations/data-model/service.mdx index 9c53f92..7a2827b 100644 --- a/nsdk/explanations/data-model/service.mdx +++ b/nsdk/explanations/data-model/service.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Service A Service is an encryptable nested entity that represents all the medical data produced during a [Contact](/explanations/data-model/contact), and is therefore diff --git a/nsdk/explanations/data-model/subcontact.mdx b/nsdk/explanations/data-model/subcontact.mdx index fbef060..34d3256 100644 --- a/nsdk/explanations/data-model/subcontact.mdx +++ b/nsdk/explanations/data-model/subcontact.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # SubContact A SubContact is an encryptable nested entity that is used to link [Contact](/explanations/data-model/contact)s and diff --git a/nsdk/explanations/data-model/timetable.mdx b/nsdk/explanations/data-model/timetable.mdx deleted file mode 100644 index 9ad3200..0000000 --- a/nsdk/explanations/data-model/timetable.mdx +++ /dev/null @@ -1,44 +0,0 @@ -# Timetable - -The timetable is an encryptable root-level entity that describes a schedule window for taking appointments. - -## Fields Encrypted by Default - -By default, no field of this entity will be encrypted. - -You can customize the encrypted fields as [explained in this how to](/how-to/initialize-the-sdk/configure-what-to-encrypt). - -## Properties - -### name - -A human-readable description for the timetable. - -### agendaId - -The id of the Agenda to which the timetable relates. - -### startTime - -The time of the first possible appointment for this timetable. This is a long formatted as YYYYMMDDHHmmss. - -### endTime - -The time of the last possible appointment for this timetable. This is a long formatted as YYYYMMDDHHmmss. - -### items - -A collection of TimetableItems that represent the available schedules for appointments in this timetable. - -A TimetableItem is a nested entity that contains the following properties: - -- `rrule`: A RRule as described in https://datatracker.ietf.org/doc/html/rfc5545 that represents the recurrence rule for this schedule. -- `notBeforeInMinutes`: The minimum time in minutes before the appointment that the patient can book it. -- `notAfterInMinutes`: The maximum time in minutes before the appointment that the patient can book it. -- `zoneId`: The id of the time zone where the schedule is defined. -- `hours`: A collection of TimeTableHour that represents the available hours ranges for the appointment. - -A TimeTableHour is a nested entity that contains the following properties: - -- `startHour`: The start time of the available hours range. This is a long formatted as HHmmss. -- `endHour`: The end time of the available hours range. This is a long formatted as HHmmss. diff --git a/nsdk/explanations/data-model/topic.mdx b/nsdk/explanations/data-model/topic.mdx index 11f7111..0c31682 100644 --- a/nsdk/explanations/data-model/topic.mdx +++ b/nsdk/explanations/data-model/topic.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # Topic A Topic is an encryptable, root-level entity that can be used in combination with the [Message](/explanations/data-model/message) diff --git a/nsdk/explanations/data-model/user.mdx b/nsdk/explanations/data-model/user.mdx index 7f8e29d..ba4b70f 100644 --- a/nsdk/explanations/data-model/user.mdx +++ b/nsdk/explanations/data-model/user.mdx @@ -1,3 +1,7 @@ +--- +sidebar_position: 0 +--- + # User A User is a non-encryptable, root-level entity that represents an actor that can log in to the system and perform actions diff --git a/nsdk/explanations/end-to-end-encryption/index.mdx b/nsdk/explanations/end-to-end-encryption/index.mdx index 722e651..26382be 100644 --- a/nsdk/explanations/end-to-end-encryption/index.mdx +++ b/nsdk/explanations/end-to-end-encryption/index.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 0 +sidebar_position: 20 --- # End-to-End Encryption diff --git a/nsdk/how-to/setup-agenda-system.mdx b/nsdk/how-to/setup-agenda-system.mdx new file mode 100644 index 0000000..8be039b --- /dev/null +++ b/nsdk/how-to/setup-agenda-system.mdx @@ -0,0 +1,338 @@ +import TabItem from "@theme/TabItem"; +import {LanguageTabs} from "@site/src/components/LanguageTabs"; + +# Set up a Basic Agenda System + +This guide walks you through configuring a simple agenda system in Cardinal, where doctors define their schedules and +patients can book appointments during the available times. + +:::info + +For a deeper explanation of how Cardinal’s agenda system works, or for guidance on setting up more complex +configurations, refer to the [Agenda System explanation page](../explanations/agenda). + +::: + +## Creating the schedules for a doctor + +To define the schedules of a doctor you use `Agenda` and `CalendarItemType` entities. +- `CalendarItemType`s are used to represent different kinds of appointments offered by doctors (e.g. General + consultation, Vaccination / immunization, ...) +- `Agenda`s define the times when doctors are available for appointments. + + + + + +```kotlin +val generalConsultationType = api.calendarItemType.createCalendarItemType( + CalendarItemType( + id = defaultCryptoService.strongRandom.randomUUID(), + duration = 20, // Duratio for appointments of this type In minutes + name = "General consultation" + ) +) +val agenda = api.agenda.createAgenda( + Agenda( + id = defaultCryptoService.strongRandom.randomUUID(), + userRights = mapOf(currentUserId to UserAccessLevel.Admin), + schedules = listOf( + ResourceGroupAllocationSchedule( + items = listOf( + EmbeddedTimeTableItem( + calendarItemTypesIds = setOf(generalConsultationType.id), + rrule = "FREQ=WEEKLY;BYDAY=MO,WE,FR", + notBeforeInMinutes = 60 * 24 * 31, + notAfterInMinutes = 60, + hours = listOf( + EmbeddedTimeTableHour(9_00_00, 13_00_00), + EmbeddedTimeTableHour(14_00_00, 18_00_00), + ), + public = true + ), + EmbeddedTimeTableItem( + calendarItemTypesIds = setOf(generalConsultationType.id), + rrule = "FREQ=WEEKLY;BYDAY=TU,TH", + notBeforeInMinutes = 60 * 24 * 31, + notAfterInMinutes = 60, + hours = listOf( + EmbeddedTimeTableHour(9_00_00, 13_00_00), + ), + public = true + ) + ) + ) + ), + slottingAlgorithm = AgendaSlottingAlgorithm.FixedIntervals(20), + publicBookingQuota = 5, + properties = setOf( + DecryptedPropertyStub( + id = "practitioner", + typedValue = DecryptedTypedValue( + stringValue = currentHcpId + ) + ) + ) + ) +) +``` + + + + + +In this example we set up an agenda for the current HCP: +- The `userRights` specify which users can read the non-public information of this agenda, and which users can modify it +- The schedules specify the times of the agenda, through the `ResourceGroupAllocationSchedule` and + `EmbeddedTimeTableItem` (for more info on `ResourceGroupAllocationSchedule` refer to the [agenda explanation page](../explanations/agenda)) + - We use two timetable items to represent differences in schedules between Monday / Wednesday / Friday and Tuesday / Thursday + - The `rrule` specifies the days of validity of the timetable items. The syntax used is the one specified by the [iCalendar RFC](https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.10). + Note that this `rrule` is not allowed to specify time information, but only dates/days information. + - The `hours` are used to specify the hours when the schedule is active. These are ranges of [FuzzyTime with second precision](../explanations/data-model/fuzzy_date_time). + - Both timetable items are public, meaning all users can see availabilities and book appointments during the specified + times + - During these timetable items users can only take appointments for the *General consultation* calendar item type + - The `notBeforeInMinutes` and `notAfterInMinutes` set limits on how early or late users can take appointments on + this agenda. In this example users will not be able to take appointments more than 31 days before the appointment's + date or less than 60 minutes before it. +- The `slottingAlgorithm` specifies how availabilities for this agenda should be converted into bookable slots. The + `FixedIntervals` algorithm will divide a range of available times into slots of equal length. In this example the + slots are 20 minutes long, which ensures each slot can fit perfectly a *General consultation* appointment. +- The `publicBookingQuota` allows to limit the amount of appointments each user can take on this agenda each month. + Note that the quota considers the time when the appointment is taken, not the time when the appointment will be + performed. +- The `properties` can be used when searching agendas. In this example we want to allow users to search for the agendas + of certain HCPs, therefore, we create a property with the HCP id in the agenda. + +## Searching for agendas and querying availabilities + +We want to let users search for HCPs, their agendas and respective availabilities. + +One option to do this is to mark the HCPs with agendas as public. +This will allow even unregistered users to search the HCPs and find the availabilities. + +:::note + +Registration is still required to book an appointment after finding the availability. + +::: + + + + + +```kotlin +api.healthcareParty.modifyHealthcareParty( + api.healthcareParty.getCurrentHealthcareParty().copy(public = true) +) +``` + + + + + +You can then use the anonymous apis to: +1. Search for the public hcps in a group +2. Search for agendas where the practitioner is the chosen hcp +3. Query the availabilities of the agenda + + + + + +```kotlin +val anonymousApis = CardinalAnonymousSdk.initialize("https://api.icure.cloud") +val allPublicHcps = anonymousApis.healthcareParty.getPublicHealthcarePartiesInGroup(groupId) +val chosenHcp: HealthcareParty = queryUserForHcpChoice(allPublicHcps) // Implement this yourself +val publicAgendas = anonymousApis.agenda.listAnonymousAgendaAndAppointmentTypes( + groupId = groupId, + propertyId = "practitioner", + propertyValue = chosenHcp.id +) +val chosenAgenda: Agenda = queryUserForAgendaChoice(publicAgendas) // Implement this yourself +val availabilities = anonymousApis.agenda.listAnonymousAvailabilities( + groupId = groupId, + agendaId = chosenAgenda.id, + calendarItemTypeId = generalConsultationTypeId, + startDate = 2025_09_01_00_00_00, + endDate = 2025_10_01_00_00_00, +) +``` + + + + + +
+ +Get public healthcare parties result + +```json +[ + { + "id":"b75518e6-9383-49f4-ac80-f31305e3899c", + "lastName":"Doe", + "firstName":"John", + "addresses":[ + { + "addressType":"work", + "telecoms":[ + { + "telecomType":"email", + "telecomNumber":"john.doe@icure.com" + } + ] + } + ] + } +] +``` + +
+ +
+ +Get anonymous agendas result + +```json +{ + "agendas":[ + { + "id":"7f067362-4fad-4da4-9ae2-1b5cf7c2412c", + "slottingAlgorithm":{ + "type":"FixedIntervals", + "intervalMinutes":20 + }, + "properties":[ + { + "id":"practitioner", + "typedValue":{ + "stringValue":"b75518e6-9383-49f4-ac80-f31305e3899c" + } + } + ], + "schedules":[ + { + "items":[ + { + "rrule":"FREQ=WEEKLY;BYDAY=MO,WE,FR", + "hours":[ + { + "startHour":90000, + "endHour":130000 + } + ], + "calendarItemTypesIds":[ + "ddae3141-c106-4e9b-b73e-ba9abc81a263" + ], + "public":true + }, + { + "rrule":"FREQ=WEEKLY;BYDAY=TU,TH", + "hours":[ + { + "startHour":90000, + "endHour":130000 + }, + { + "startHour":140000, + "endHour":180000 + } + ], + "calendarItemTypesIds":[ + "ddae3141-c106-4e9b-b73e-ba9abc81a263" + ], + "public":true + } + ] + } + ] + } + ], + "calendarItemTypes":[ + { + "id":"ddae3141-c106-4e9b-b73e-ba9abc81a263", + "name":"General consultation", + "duration":20 + } + ] +} +``` + +
+ +
+ +Get availabilities result + +```json +[ + 20250901090000, + 20250901092000, + 20250901094000, + 20250901100000, + 20250901102000, + // ... +] +``` + +
+ +## Booking an appointment + +After finding availability registered users can book appointments using the `CalendarItemApi.bookCalendarItemCheckingAvailability` method. + +This method requires that the calendar item specifies values for `agendaId`, `calendarItemTypeId`, and `startTime` that +match the availability. For this reason these properties can't be encrypted. + + + + + +```kotlin +val self = (patientApi.dataOwner.getCurrentDataOwner() as DataOwnerWithType.PatientDataOwner).dataOwner +patientApi.calendarItem.bookCalendarItemCheckingAvailability( + patientApi.calendarItem.withEncryptionMetadata( + DecryptedCalendarItem( + id = defaultCryptoService.strongRandom.randomUUID(), + agendaId = agendaId, + calendarItemTypeId = generalConsultationTypeId, + startTime = 2025_09_01_09_40_00, + ), + self, + secretId = SecretIdUseOption.UseNone + ) +) +``` + + + + + +:::note + +If you are not sure whether the patient will have access to its own secret ids or not you can use +`SecretIdUseOption.UseNone` when initializing the calendar item encryption metadata. + +You will still be able to find the patient of a calendar item, however you won't be able to search all calendar items +linked to a patient unless a separate process with access to the calendar item and corresponding patient secret ids +fills the `secretForeignKeys`. + +::: + +## Booking appointments without initializing an encryption key for the patient + +Normally in Cardinal each data owner, whether he is an HCP, patient or device, has a personal encryption keypair. +This, however, requires that the users all safely store the keypair, without losing it or leaking it to third parties. + +If in your application patients are only allowed to create calendar items and never access any medical data, it might +be better to use the *keyless api* feature for patient users. +This allows the patients to create data and share it with healthcare parties without having to create and manage a +personal keypair. + +:::info + +:construction: A documentation page for the keyless API mode is currently under construction. If you want to know more +about this feel free to contact us directly :construction: + +:::