Created
January 11, 2021 17:18
-
-
Save andrewrlee/681f62f9cb27261e899bc79649d842f9 to your computer and use it in GitHub Desktop.
Representing appointments as a timeline
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.LocalTime | |
fun isAvailable(request: Request) { | |
val timeLine = createTimeLine(LocalTime.of(8, 0), LocalTime.of(18, 0), 5) | |
val bookings = getTodaysBookings() // listOf(RoomBooking(Room.ONE, TimeRange("2007-12-03T10:10:00", "2007-12-03T10:40:00"))) | |
bookings.filter { notPartOf(request.bookingToAmmend) }. forEach { timeLine.add(it) } | |
return timeLine.isAvailable(request) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.LocalTime | |
fun main() { | |
val timeLine = createTimeLine(LocalTime.of(4, 0), LocalTime.of(20, 0), 5) | |
timeLine.add(RoomBooking(Room.ONE, TimeRange("2007-12-03T10:10:00", "2007-12-03T10:40:00"))) | |
timeLine.add(RoomBooking(Room.ONE, TimeRange("2007-12-03T10:50:00", "2007-12-03T11:10:00"))) | |
timeLine.add(RoomBooking(Room.TWO, TimeRange("2007-12-03T10:20:00", "2007-12-03T10:50:00"))) | |
timeLine.add(RoomBooking(Room.THREE, TimeRange("2007-12-03T09:50:00", "2007-12-03T10:05:00"))) | |
println(timeLine.isAvailable(RoomBooking(Room.ONE, TimeRange("2007-12-03T09:10:00", "2007-12-03T09:40:00")))) | |
println(timeLine.isAvailable(RoomBooking(Room.ONE, TimeRange("2007-12-03T10:10:00", "2007-12-03T10:40:00")))) | |
println(timeLine.isAvailable(RoomBooking(Room.ONE, TimeRange("2007-12-03T10:40:00", "2007-12-03T10:45:00")))) | |
println(timeLine.isAvailable(RoomBooking(Room.ONE, TimeRange("2007-12-03T10:40:00", "2007-12-03T10:50:00")))) | |
println(timeLine.isAvailable(RoomBooking(Room.ONE, TimeRange("2007-12-03T10:50:00", "2007-12-03T10:55:00")))) | |
println(timeLine.getBookedRoomsBetween(TimeRange("2007-12-03T09:30:00", "2007-12-03T10:50:00"))) | |
val roomRequest = RoomRequest( | |
pre = RoomBooking(Room.ONE, TimeRange("2007-12-03T10:00:00", "2007-12-03T10:20:00")), | |
main = RoomBooking(Room.ONE, TimeRange("2007-12-03T10:20:00", "2007-12-03T10:40:00")), | |
post = RoomBooking(Room.ONE, TimeRange("2007-12-03T10:40:00", "2007-12-03T11:00:00")) | |
) | |
println(timeLine.isAvailable(roomRequest)) | |
println(timeLine.findAvailableRooms(roomRequest.toRequest(), Room.values().toSet())) | |
println(timeLine.availableToday(roomRequest.toRequest(), Room.values().toSet())) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.Duration | |
import java.time.LocalDate | |
import java.time.LocalDateTime | |
import java.time.LocalTime | |
import java.util.TreeMap | |
fun LocalTime.minutesOfDay(): Long = Duration.between(LocalTime.MIDNIGHT, this).toMinutes() | |
fun LocalDateTime.minutesOfDay(): Long = this.toLocalTime().minutesOfDay() | |
typealias TimeLine = TreeMap<Long, MutableList<Room>> | |
enum class Room { ONE, TWO, THREE, FOUR } | |
data class TimeRange(val startTime: LocalDateTime, val endTime: LocalDateTime) { | |
constructor(startTime: String, endTime: String) : this(LocalDateTime.parse(startTime), LocalDateTime.parse(endTime)) | |
val start: Long get() = startTime.minutesOfDay() | |
val end: Long get() = endTime.minutesOfDay() | |
fun adjust(duration: Duration) = TimeRange(startTime.minus(duration), endTime.minus(duration)) | |
} | |
data class RoomBooking(val room: Room, val timeRange: TimeRange) { | |
val start: Long get() = timeRange.start | |
val end: Long get() = timeRange.end | |
} | |
data class RoomRequest(val pre: RoomBooking?, val main: RoomBooking, val post: RoomBooking?) { | |
fun toRequest() = Request(main = main.timeRange, pre = pre?.timeRange, post = post?.timeRange) | |
} | |
data class AvailableRooms(val pre: List<Room>?, val main: Room, val post: List<Room>?) | |
data class Request(val main: TimeRange, val pre: TimeRange?, val post: TimeRange?) { | |
fun move(startTime: LocalDateTime): Request { | |
val earliestDate = pre?.startTime ?: main.startTime | |
val adjustment = Duration.between(startTime, earliestDate) | |
return Request( | |
pre = pre?.adjust(adjustment), | |
main = main.adjust(adjustment), | |
post = post?.adjust(adjustment)) | |
} | |
} | |
fun createTimeLine(startOfDay: LocalTime, endOfDay: LocalTime, interval: Long): TimeLine { | |
val indices = (startOfDay.minutesOfDay()..endOfDay.minutesOfDay() step interval) | |
return indices.map { Pair(it, mutableListOf<Room>()) }.toMap(TreeMap()) | |
} | |
fun TimeLine.add(booking: RoomBooking) = (booking.start until booking.end).forEach { slot -> | |
this.computeIfPresent(slot) { _, list -> list.also { it.add(booking.room) } } | |
} | |
fun TimeLine.getBookedRoomsBetween(timeRange: TimeRange) = this.subMap(timeRange.start, timeRange.end).flatMap { it.value }.toSortedSet() | |
fun TimeLine.isAvailable(booking: RoomBooking) = booking.room !in this.getBookedRoomsBetween(booking.timeRange) | |
fun TimeLine.isAvailable(request: RoomRequest): Boolean { | |
val mainAvailable = isAvailable(request.main) | |
val preAvailable = request.pre?.let { isAvailable(it) } ?: true | |
val postAvailable = request.post?.let { isAvailable(it) } ?: true | |
return mainAvailable && preAvailable && postAvailable | |
} | |
fun TimeLine.findAvailableRooms(request: Request, rooms: Collection<Room>): AvailableRooms? { | |
val availableForPre = request.pre?.let { rooms - getBookedRoomsBetween(it) } | |
val availableForMain = rooms - getBookedRoomsBetween(request.main) | |
val availableForPost = request.post?.let { rooms - getBookedRoomsBetween(it) } | |
return availableForMain.map { room -> | |
val leftOverForPre = availableForPre?.let { it - room } | |
val leftOverForPost = availableForPost?.let { it - room } | |
AvailableRooms(leftOverForPre, room, leftOverForPost) | |
}.find { (leftOverForPre, _, leftOverForPost) -> | |
(leftOverForPre == null || leftOverForPre.isNotEmpty()) && (leftOverForPost == null || leftOverForPost.isNotEmpty()) | |
} | |
} | |
fun TimeLine.availableToday(request: Request, rooms: Collection<Room>): Boolean { | |
val availableSlots = this.keys | |
.map { | |
val newStartTime = LocalDateTime.of(LocalDate.now(), LocalTime.ofSecondOfDay(it * 60)) // default to localdate.now for convenience? move to just work on times? | |
val availableRoomsAtTime = findAvailableRooms(request.move(newStartTime), rooms) | |
Pair(it, availableRoomsAtTime) | |
} | |
.filter { it.second != null } | |
println(availableSlots) | |
// TODO need to ensure bookings that finish after closed time aren't counted | |
return availableSlots.isNotEmpty() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment