Skip to content

Commit

Permalink
DRTII-1561 Use indexes on assignment to improve filter performance (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
richbirch authored Feb 6, 2025
1 parent 6822a87 commit e356dae
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 36 deletions.
4 changes: 2 additions & 2 deletions client/src/main/scala/drt/client/SPAMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,9 @@ object SPAMain {

def loadAction: Action = mode match {
case Staffing =>
GetAllShifts
GetAllLegacyStaffAssignments
case Shifts =>
GetAllStaffShifts
GetAllStaffAssignments
case _ =>
SetViewMode(viewMode)
}
Expand Down
4 changes: 2 additions & 2 deletions client/src/main/scala/drt/client/actions/Actions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ object Actions {

case class SetAllShifts(allShifts: ShiftAssignments) extends Action

case object GetAllShifts extends Action
case object GetAllLegacyStaffAssignments extends Action

case class UpdateShifts(shiftsToUpdate: Seq[StaffAssignment]) extends Action

Expand All @@ -105,7 +105,7 @@ object Actions {

case class SetAllStaffShifts(allShifts: ShiftAssignments) extends Action

case object GetAllStaffShifts extends Action
case object GetAllStaffAssignments extends Action

case class UpdateStaffShifts(shiftsToUpdate: Seq[StaffAssignment]) extends Action

Expand Down
11 changes: 4 additions & 7 deletions client/src/main/scala/drt/client/components/MonthlyShifts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package drt.client.components
import diode.AnyAction.aType
import diode.data.Pot
import drt.client.SPAMain.{Loc, TerminalPageTabLoc, ShiftViewEnabled, UrlDateParameter, UrlDayRangeType}
import drt.client.actions.Actions.{GetAllStaffShifts, UpdateStaffShifts}
import drt.client.actions.Actions.{GetAllStaffAssignments, UpdateStaffShifts}
import drt.client.components.MonthlyShiftsUtil.{generateShiftSummaries, updateAssignments, updateChangeAssignment}
import drt.client.components.StaffingUtil.navigationDates
import drt.client.logger.{Logger, LoggerFactory}
Expand Down Expand Up @@ -145,14 +145,15 @@ object MonthlyShifts {
staffShifts <- model.staffShiftsPot
} yield {
if (monthOfShifts != state.shifts) {
val initialShift: Seq[ShiftSummaryStaffing] = MonthlyShiftsUtil.generateShiftSummaries(viewingDate,
val shiftSummaries = MonthlyShiftsUtil.generateShiftSummaries(
viewingDate,
props.terminalPageTab.dayRangeType.getOrElse("monthly"),
props.terminalPageTab.terminal,
staffShifts,
ShiftAssignments(monthOfShifts.forTerminal(props.terminalPageTab.terminal)),
props.timeSlotMinutes)
scope.modState(state => state.copy(shifts = monthOfShifts,
shiftsData = initialShift)).runNow()
shiftsData = shiftSummaries)).runNow()
}
<.div()
}
Expand Down Expand Up @@ -336,24 +337,20 @@ object MonthlyShifts {
)
}


val component: Component[Props, State, Backend, CtorType.Props] = ScalaComponent.builder[Props]("ShiftStaffing")
.initialStateFromProps(_ => State(showEditStaffForm = false,
showStaffSuccess = false,
addShiftForm = false,
shifts = ShiftAssignments.empty))
.renderBackend[Backend]
.configure(Reusability.shouldComponentUpdate)
.componentDidMount(_ => Callback(SPACircuit.dispatch(GetAllStaffShifts)))
.build


private def updatedConvertedShiftAssignments(changes: Seq[StaffAssignment],
terminalName: Terminal): Seq[StaffAssignment] = changes.map { change =>
StaffAssignment(change.name, terminalName, change.start, change.end, change.numberOfStaff, None)
}


private def maybeClockChangeDate(viewingDate: SDateLike): Option[SDateLike] = {
val lastDay = SDate.lastDayOfMonth(viewingDate)
(0 to 10).map(offset => lastDay.addDays(-1 * offset)).find(date => slotsInDay(date, 60).length == 25)
Expand Down
45 changes: 27 additions & 18 deletions client/src/main/scala/drt/client/components/MonthlyShiftsUtil.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package drt.client.components

import drt.client.services.JSDateConversions.SDate
import drt.shared.{ShiftAssignments, StaffAssignmentLike, Shift}
import drt.shared.{Shift, ShiftAssignments, StaffAssignmentLike}
import uk.gov.homeoffice.drt.ports.Terminals.Terminal
import uk.gov.homeoffice.drt.time.SDateLike
import uk.gov.homeoffice.drt.time.{LocalDate, SDateLike}

import scala.scalajs.js.Date


Expand Down Expand Up @@ -54,19 +55,22 @@ object MonthlyShiftsUtil {
def createStaffTableEntries(startDate: SDateLike,
daysCount: Int,
interval: Int,
shiftDetails: ShiftDetails
shiftDetails: ShiftDetails,
assignmentsByDate: Map[LocalDate, Seq[StaffAssignmentLike]],
): Seq[StaffTableEntry] = {

val Array(shiftStartHour, shiftStartMinute) = shiftDetails.shift.startTime.split(":").map(_.toInt)
val Array(shiftEndHour, shiftEndMinute) = shiftDetails.shift.endTime.split(":").map(_.toInt)
var currentDay = startDate

val isShiftEndAfterMidnight = shiftEndHour < shiftStartHour || (shiftEndHour == shiftStartHour && shiftEndMinute < shiftStartMinute)
//For all the days in the period, create the staff table entries for the shift
(1 to daysCount).flatMap { day =>
val currentDay = startDate.addDays(day - 1)
val shiftStartTime = SDate(currentDay.getFullYear, currentDay.getMonth, currentDay.getDate, shiftStartHour, shiftStartMinute)
val shiftEndTime = SDate(currentDay.getFullYear, currentDay.getMonth, currentDay.getDate, shiftEndHour, shiftEndMinute)
val midnightNextDay = SDate(currentDay.getFullYear, currentDay.getMonth, currentDay.getDate, 0, 0).addDays(1)

val assignments = assignmentsByDate.getOrElse(currentDay.toLocalDate, Seq.empty)

val beforeMidnightPeriod = ShiftPeriod(
start = shiftStartTime,
end = if (isShiftEndAfterMidnight) midnightNextDay else shiftEndTime,
Expand All @@ -89,11 +93,11 @@ object MonthlyShiftsUtil {
isFirstDayForShiftEndAfterMidnight = true,
addToIndex = 0
)

staffTableEntriesForShift(firstDayMidnightToStartTimePeriod, shiftDetails)
staffTableEntriesForShift(firstDayMidnightToStartTimePeriod, shiftDetails, assignments)
} else Seq.empty

val beforeMidnightEntries = staffTableEntriesForShift(beforeMidnightPeriod.copy(addToIndex = beforeFirstDateStartTime.size), shiftDetails)
val period = beforeMidnightPeriod.copy(addToIndex = beforeFirstDateStartTime.size)
val beforeMidnightEntries = staffTableEntriesForShift(period, shiftDetails, assignments)

val afterMidnightEntries = if (isShiftEndAfterMidnight) {
val midnightStart = midnightNextDay
Expand All @@ -103,16 +107,15 @@ object MonthlyShiftsUtil {
isFirstDayForShiftEndAfterMidnight = false,
addToIndex = beforeMidnightEntries.size
)
staffTableEntriesForShift(afterMidnightPeriod, shiftDetails)
staffTableEntriesForShift(afterMidnightPeriod, shiftDetails, assignments)
} else Seq.empty

currentDay = currentDay.addDays(1)
beforeFirstDateStartTime ++ beforeMidnightEntries ++ afterMidnightEntries
}
}

def staffTableEntriesForShift(shiftPeriod: ShiftPeriod, shiftDetails: ShiftDetails): Seq[StaffTableEntry] = {
val dayAssignments = shiftDetails.shiftAssignments.assignments
def staffTableEntriesForShift(shiftPeriod: ShiftPeriod, shiftDetails: ShiftDetails, assignments: Seq[StaffAssignmentLike]): Seq[StaffTableEntry] = {
val dayAssignments = assignments
.filter(assignment => assignment.start >= shiftPeriod.start.millisSinceEpoch && assignment.end <= shiftPeriod.end.millisSinceEpoch)

Iterator.iterate(shiftPeriod.start) { intervalTime =>
Expand Down Expand Up @@ -169,14 +172,20 @@ object MonthlyShiftsUtil {
shifts: Seq[Shift],
shiftAssignments: ShiftAssignments,
interval: Int): Seq[ShiftSummaryStaffing] = {
shifts.sortBy(_.startTime).zipWithIndex.map { case (s, index) =>
val assignmentsByDate: Map[LocalDate, Seq[StaffAssignmentLike]] = shiftAssignments.assignments.groupBy(sa => SDate(sa.start).toLocalDate)

shifts.sortBy(_.startTime).zipWithIndex.map { case (shift, index) =>
val tableEntries = createStaffTableEntries(
firstDayByDayRange(dayRange, viewingDate),
daysCountByDayRange(dayRange, viewingDate),
interval,
ShiftDetails(shift, terminal, shiftAssignments),
assignmentsByDate,
)
ShiftSummaryStaffing(
index = index,
shiftSummary = ShiftSummary(s.shiftName, s.staffNumber, s.startTime, s.endTime),
staffTableEntries = createStaffTableEntries(firstDayByDayRange(dayRange, viewingDate),
daysCountByDayRange(dayRange, viewingDate),
interval,
ShiftDetails(s, terminal, shiftAssignments))
shiftSummary = ShiftSummary(shift.shiftName, shift.staffNumber, shift.startTime, shift.endTime),
staffTableEntries = tableEntries
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package drt.client.components
import diode.AnyAction.aType
import diode.data.{Empty, Pot, Ready}
import drt.client.SPAMain.{Loc, TerminalPageTabLoc, ShiftViewEnabled, UrlDateParameter, UrlDayRangeType}
import drt.client.actions.Actions.{GetAllStaffShifts, UpdateShifts, UpdateStaffShifts}
import drt.client.actions.Actions.{GetAllStaffAssignments, UpdateShifts, UpdateStaffShifts}
import drt.client.components.StaffingUtil.{consecutiveDayForWeek, consecutiveDaysInMonth, dateRangeDays, navigationDates}
import drt.client.logger.{Logger, LoggerFactory}
import drt.client.modules.GoogleEventTracker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ object TerminalComponent {
rcp { mp =>
val (mt, ps, ai, slas, manSums, arrSources, simRes, fhl) = mp()

val hideAddShiftsMessage = shifts.nonEmpty || !featureFlags.enableShiftPlanningChange

props.terminalPageTab.mode match {
case Current =>
val headerClass = if (terminalModel.timeMachineEnabled) "terminal-content-header__time-machine" else ""
Expand Down Expand Up @@ -234,11 +236,11 @@ object TerminalComponent {
case Staffing if loggedInUser.roles.contains(StaffEdit) && props.terminalPageTab.subMode == "createShifts" =>
<.div(drt.client.components.ShiftsComponent(props.terminalPageTab.terminal, props.terminalPageTab.portCodeStr, props.router))
case Staffing if loggedInUser.roles.contains(StaffEdit) =>
<.div(MonthlyStaffing(props.terminalPageTab, props.router, airportConfig, (shifts.nonEmpty || !featureFlags.enableShiftPlanningChange), false))
<.div(MonthlyStaffing(props.terminalPageTab, props.router, airportConfig, hideAddShiftsMessage, false))

case Shifts if loggedInUser.roles.contains(StaffEdit) && shifts.nonEmpty =>
if (props.terminalPageTab.shiftViewEnabled)
<.div(MonthlyStaffing(props.terminalPageTab, props.router, airportConfig, (shifts.nonEmpty || !featureFlags.enableShiftPlanningChange), true))
<.div(MonthlyStaffing(props.terminalPageTab, props.router, airportConfig, hideAddShiftsMessage, true))
else
<.div(MonthlyShifts(props.terminalPageTab, props.router, airportConfig))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package drt.client.services.handlers

import diode._
import diode.data.{Pending, Pot, Ready}
import drt.client.actions.Actions.{GetAllShifts, GetAllStaffShifts, SetAllShifts, SetAllStaffShifts}
import drt.client.actions.Actions.{GetAllLegacyStaffAssignments, GetAllStaffAssignments, SetAllShifts, SetAllStaffShifts}
import drt.client.logger.log
import drt.client.services.DrtApi
import drt.shared.ShiftAssignments
Expand All @@ -13,7 +13,7 @@ import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue

class AllStaffAssignmentsHandler[M](modelRW: ModelRW[M, Pot[ShiftAssignments]]) extends LoggingActionHandler(modelRW) {
protected def handle: PartialFunction[Any, ActionResult[M]] = {
case GetAllStaffShifts =>
case GetAllStaffAssignments =>
val apiCallEffect = Effect(DrtApi.get("staff-assignments")
.map(r => SetAllStaffShifts(read[ShiftAssignments](r.responseText)))
.recoverWith {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package drt.client.services.handlers

import diode._
import diode.data.{Pending, Pot, Ready}
import drt.client.actions.Actions.{GetAllShifts, SetAllShifts}
import drt.client.actions.Actions.{GetAllLegacyStaffAssignments, SetAllShifts}
import drt.client.logger.log
import drt.client.services.DrtApi
import drt.shared.ShiftAssignments
Expand All @@ -13,7 +13,7 @@ import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue

class LegacyAllStaffAssignmentsHandler[M](modelRW: ModelRW[M, Pot[ShiftAssignments]]) extends LoggingActionHandler(modelRW) {
protected def handle: PartialFunction[Any, ActionResult[M]] = {
case GetAllShifts =>
case GetAllLegacyStaffAssignments =>
val apiCallEffect = Effect(DrtApi.get("legacy-staff-assignments")
.map(r => SetAllShifts(read[ShiftAssignments](r.responseText)))
.recoverWith {
Expand Down

0 comments on commit e356dae

Please sign in to comment.