codex_services.booking._shared.validators
validators
codex_services.booking._shared.validators
Validators for ensuring booking data correctness.
Used by ChainFinder to verify found solutions, and can also be used independently in tests or the service layer.
Imports
from codex_services.booking import BookingValidator
Classes
BookingValidator
A set of correctness checks for booking data. Unaffected by the ORM — operates exclusively on DTOs.
Used by
- ChainFinder: Ensuring found chains have no conflicts.
- Adapter: Final verification before creating Appointment instances in the DB.
- Tests: Isolated logic verification without Django.
Example
v = BookingValidator()
Check if a slot is free:
ok = v.is_slot_free( slot_start=datetime(2024,5,10,10,0), slot_end=datetime(2024,5,10,11,0), busy_intervals=[(datetime(2024,5,10,9,0), datetime(2024,5,10,9,30))], )
→ True (no overlap)
Check entire chain for conflicts:
ok = v.no_conflicts(solutions)
Source code in src/codex_services/booking/_shared/validators.py
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |
Functions
is_slot_free(slot_start, slot_end, busy_intervals)
Verifies that the slot [slot_start, slot_end) does not overlap with any of the busy intervals.
Uses a "half-open" interval — [start, end). If slot_end == busy_start, it is NOT considered a conflict (adjacent slots are allowed).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
slot_start
|
datetime
|
Start of the slot to check. |
required |
slot_end
|
datetime
|
End of the slot to check. |
required |
busy_intervals
|
list[tuple[datetime, datetime]]
|
List of busy intervals [(start, end), ...]. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the slot is free. False if there is an overlap. |
Example
Busy 10:00-11:00. Requesting 10:30-11:30 → conflict:
is_slot_free(10:30, 11:30, [(10:00, 11:00)]) → False
Requesting 11:00-12:00 → OK (adjacent slots):
is_slot_free(11:00, 12:00, [(10:00, 11:00)]) → True
Source code in src/codex_services/booking/_shared/validators.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
no_conflicts(solutions)
Verifies that there are no resource conflicts within a set of solutions. A resource cannot be occupied by two services simultaneously.
Groups solutions by resource_id and checks each group for overlaps. Used by ChainFinder after assembling the chain for final verification.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
solutions
|
list[BookingSolution]
|
List of BookingSolution objects (found slots). |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if no conflicts exist. False if at least one resource is double-booked. |
Example
no_conflicts([ SingleServiceSolution(resource_id="1", start=9:00, gap_end=10:10), SingleServiceSolution(resource_id="1", start=10:10, gap_end=11:10), ])
→ True (slots are adjacent, no overlap)
Source code in src/codex_services/booking/_shared/validators.py
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
solution_fits_in_windows(solution, free_windows)
Verifies that a solution's slot fits entirely inside one of the resource's free windows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
solution
|
BookingSolution
|
Found slot for a single service. |
required |
free_windows
|
list[tuple[datetime, datetime]]
|
Resource's free windows (from ResourceAvailability). |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the slot fits perfectly inside one of the given free windows. |
Source code in src/codex_services/booking/_shared/validators.py
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |