michel 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +92 -4
- data/SECURITY.md +18 -0
- data/lib/michel/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 729ce5f1ae788f0942ab2e59cd5dc6f1187e881259677a7c7894b32495cf477d
|
|
4
|
+
data.tar.gz: 7f5daf203587424d2817f51e591f2d1f9a94131f33ffe7e6eecb0fcb73a9cfda
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e13696e4430a7c9dbd5720486999770d3396f93e06d2873c22a1bfa032de8862fcd63b3357830a5385963bd0e9f6f278145f7125efb91057080ca0680cb6765
|
|
7
|
+
data.tar.gz: 99fc0c5607d940b4569539b6583402211b202bb616d8d29e89bb1b0881b2553f76ff9737d1a625c1ce6e1a6490198f57fc6f4f9d365171ec082f872dca71cc0c
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -1,13 +1,79 @@
|
|
|
1
1
|
# Michel
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A generator for creating a materialized-view-backed model for querying available time slots. Inspired by and tailored for health care applications that need self-scheduling capability for users. Named for the worst scheduling assistant on tv, [Michel Gerard of Gilmore Girls.](https://gilmoregirls.fandom.com/wiki/Michel_Gerard)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## The problem
|
|
7
|
+
When scheduling things like doctor's appointments, hair cuts, or conference room bookings, answering "Is Dr. Doolittle available at this time?" is fairly straightforward. You can query whether there are existing appointments overlapping the time you're looking at, which is fairly quick to do with a well-indexed database.
|
|
8
|
+
|
|
9
|
+
Answering the same question for multiple doctors, stylists, or conference rooms - "Who is available at this time" - is slightly more complex, but still manageable with standard database query methods.
|
|
10
|
+
|
|
11
|
+
When building an app that allows self-scheduling, the question you really want to ask it "what resources are available at what times during this time period?" This is a more complicated query. Finding available time slots using Ruby is painfully slow. Postgres has some powerful capabilities that can help us, though.
|
|
12
|
+
|
|
13
|
+
## The solution
|
|
14
|
+
|
|
15
|
+
Michel helps create a materialized database view for answering this question, using the power of [Scenic](https://github.com/scenic-views/scenic) and [Postgres](https://www.postgresql.org/). Postgres has powerful tools for querying time ranges and finding overlaps. You tell Michel the class name of your resource (doctor, stylist, conference room, etc), the class name of your booking (appointment, reservation, etc) and the class name of your availability model and Michel creates a materialized view-backed model called `AvailableTimeSlot`.
|
|
16
|
+
|
|
17
|
+
These available time slots have start times every 15 minutes for 30 minute durations during times the resource is not already booked. The view currently calculates time slots for the next six months. In the future, this range will be configurable.
|
|
18
|
+
|
|
19
|
+
For example, a doctor who works 9am-12pm Eastern on Mondays would have the following available 30-minute time slots for the first full week of 2026:
|
|
20
|
+
|
|
21
|
+
* Monday, Jan 5, 2026, 9:00am EST
|
|
22
|
+
* Monday, Jan 5, 2026, 9:15am EST
|
|
23
|
+
* Monday, Jan 5, 2026, 9:30am EST
|
|
24
|
+
* Monday, Jan 5, 2026, 9:45am EST
|
|
25
|
+
* Monday, Jan 5, 2026, 10:00am EST
|
|
26
|
+
* Monday, Jan 5, 2026, 10:15am EST
|
|
27
|
+
* Monday, Jan 5, 2026, 10:30am EST
|
|
28
|
+
* Monday, Jan 5, 2026, 10:45am EST
|
|
29
|
+
* Monday, Jan 5, 2026, 11:00am EST
|
|
30
|
+
* Monday, Jan 5, 2026, 11:15am EST
|
|
31
|
+
* Monday, Jan 5, 2026, 11:30am EST
|
|
32
|
+
|
|
33
|
+
If they then scheduled a 30-minute appointment on Jan 5, 2026 starting at 9:45, the refreshed availability would be:
|
|
34
|
+
* Monday, Jan 5, 2026, 9:00am EST
|
|
35
|
+
* Monday, Jan 5, 2026, 9:15am EST
|
|
36
|
+
* Monday, Jan 5, 2026, 10:15am EST
|
|
37
|
+
* Monday, Jan 5, 2026, 10:30am EST
|
|
38
|
+
* Monday, Jan 5, 2026, 10:45am EST
|
|
39
|
+
* Monday, Jan 5, 2026, 11:00am EST
|
|
40
|
+
* Monday, Jan 5, 2026, 11:15am EST
|
|
41
|
+
* Monday, Jan 5, 2026, 11:30am EST
|
|
42
|
+
|
|
43
|
+
An appointment that ends at 10:15 and an appointment that begins at 10:15 are considered to be consecutive, not overlapping.
|
|
44
|
+
|
|
45
|
+
More details on what an availability record looks like are in the Configure section.
|
|
46
|
+
|
|
47
|
+
## Limitations
|
|
48
|
+
|
|
49
|
+
The materialized view is stored in the database and it is not a trivial size. Michel favors increasing database storage over database or application processing. If limiting database size is a higher priority than finding available time slots quickly, this is not the right tool for you. In our experience, it's cheaper and easier to scale up a database than to lose users waiting for time slots to load.
|
|
50
|
+
|
|
51
|
+
Currently, only the class names and the attributes stored on availabilities are customizable. Start times are every 15 minutes and the total range of time slots is 6 months from the current date. More configuration options will be available in the future.
|
|
52
|
+
|
|
53
|
+
The materialized view must be refreshed every time a booking or availability is created, modified, or deleted. For most applications that involve self-scheduling, a user queries the availability much more often than a booking is created or a schedule is changed, so it is much more efficient to materialize the view and refresh it regularly than to have a non-materialized view that is queried on every search.
|
|
54
|
+
|
|
55
|
+
All bookings are considered to be blocking. In order to make a time slot available, the booking must be destroyed. In the future, we intend to add a way to configure status values that are non-blocking for things like cancelled appointments where you'd like to keep the record but want the resource to be available.
|
|
56
|
+
|
|
57
|
+
Available time slots each have a 30-minute duration. This will be configurable in the future. Bookings can be any duration.
|
|
58
|
+
|
|
59
|
+
Availabilities are for only one resource type right now. A doctor who works at two different clinics, for example, will not have a way to distinguish between availabilities at different clinics. In the future, we will add support for a second resource type, so a query for available time slots can be scoped to a second resource, like a clinic location. The current view allows for this scoping only by joining to the availability table and scoping that to the second resource, which is less efficient.
|
|
60
|
+
|
|
61
|
+
## Integration with an EHR
|
|
62
|
+
|
|
63
|
+
Michel was inspired while working on a health care app that integrated with an existing EHR. The EHR was able to provide appointment times and provider schedules, but not available time slots. Michel's materialized view helped us calculate those and ensure that providers weren't double-booked via both our application and staff booking directly in the EHR. In order to make sure the two systems stay in sync, it is necessary to:
|
|
64
|
+
|
|
65
|
+
* Update the application database with booking creations/changes/deletions from the EHR as they are made. Many EHRs use webhooks for this sort of synchronization.
|
|
66
|
+
* Update the EHR with booking creations/changes/deletions from the application as they are made. This is usually accomplished with an api endpoint provided by the EHR.
|
|
67
|
+
* Update the application database with availability creations/changes/deletions from the EHR as they are made. This is usually also a webhook.
|
|
68
|
+
* Refresh the materialized view when any of the above updates are made.
|
|
69
|
+
* Treat the EHR as the source of truth for both bookings and availabilities. Be careful about allowing modifications of resource availability from both the EHR and the application, as this can be tricky to keep in sync.
|
|
4
70
|
|
|
5
71
|
## Usage
|
|
6
72
|
|
|
7
73
|
### Install Michel
|
|
8
74
|
In order to run the migrations, you must also install Scenic. Add to your gemfile:
|
|
9
75
|
|
|
10
|
-
```
|
|
76
|
+
``` ruby
|
|
11
77
|
gem "michel"
|
|
12
78
|
gem "scenic"
|
|
13
79
|
```
|
|
@@ -15,7 +81,28 @@ Find available time slots quickly with a materialized view.
|
|
|
15
81
|
Run `bundle install`, then `rails generate michel:install`
|
|
16
82
|
|
|
17
83
|
### Configure
|
|
18
|
-
Configure the class names in `config/initializers/michel.rb`
|
|
84
|
+
Configure the class names in `config/initializers/michel.rb`. The default initializer is copied to your app when you run `rake michel:install` and looks like this:
|
|
85
|
+
|
|
86
|
+
``` ruby
|
|
87
|
+
# config/initializers/michel.rb
|
|
88
|
+
|
|
89
|
+
Michel.setup do |config|
|
|
90
|
+
config.resource_class_name = "Resource"
|
|
91
|
+
config.booking_class_name = "Booking"
|
|
92
|
+
config.availability_class_name = "Availability"
|
|
93
|
+
end
|
|
94
|
+
```
|
|
95
|
+
Replace the default class names with the class names from your app, if they differ, for example:
|
|
96
|
+
|
|
97
|
+
``` ruby
|
|
98
|
+
# config/initializers/michel.rb
|
|
99
|
+
|
|
100
|
+
Michel.setup do |config|
|
|
101
|
+
config.resource_class_name = "ConferenceRoom"
|
|
102
|
+
config.booking_class_name = "Reservation"
|
|
103
|
+
config.availability_class_name = "RoomSchedule"
|
|
104
|
+
end
|
|
105
|
+
```
|
|
19
106
|
|
|
20
107
|
Michel expects three classes to exist in the application:
|
|
21
108
|
* Resource Class
|
|
@@ -51,7 +138,8 @@ Find available time slots quickly with a materialized view.
|
|
|
51
138
|
|
|
52
139
|
Once the generator is finished, run `rails db:migrate` to run the generated migrations.
|
|
53
140
|
|
|
54
|
-
### Start
|
|
141
|
+
### Start querying for Available Time Slots
|
|
142
|
+
The `AvailableTimeSlot` class stores the generated time slots that don't overlap with existing bookings.
|
|
55
143
|
To find available time slots, search for matching `AvailableTimeSlots`. Each slot has a `start_time` and an `end_time` and belongs to an `availability` and a `resource`.
|
|
56
144
|
|
|
57
145
|
To refresh the materialized view, run `AvailableTimeSlot.refresh`. The view should be refreshed when a booking is created, updated, or deleted, or when availability changes.
|
data/SECURITY.md
CHANGED
|
@@ -1,2 +1,20 @@
|
|
|
1
1
|
<!-- START /templates/security.md -->
|
|
2
|
+
# Security Policy
|
|
3
|
+
|
|
4
|
+
## Supported Versions
|
|
5
|
+
|
|
6
|
+
Only the the latest version of this project is supported at a given time. If
|
|
7
|
+
you find a security issue with an older version, please try updating to the
|
|
8
|
+
latest version first.
|
|
9
|
+
|
|
10
|
+
If for some reason you can't update to the latest version, please let us know
|
|
11
|
+
your reasons so that we can have a better understanding of your situation.
|
|
12
|
+
|
|
13
|
+
## Reporting a Vulnerability
|
|
14
|
+
|
|
15
|
+
For security inquiries or vulnerability reports, visit
|
|
16
|
+
<https://thoughtbot.com/security>.
|
|
17
|
+
|
|
18
|
+
If you have any suggestions to improve this policy, visit <https://thoughtbot.com/security>.
|
|
19
|
+
|
|
2
20
|
<!-- END /templates/security.md -->
|
data/lib/michel/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: michel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sally Hall
|
|
8
8
|
- Aji Slater
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-02-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|