Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter calibrations out of constraints summary table #4557

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
318 changes: 167 additions & 151 deletions explore/src/main/scala/explore/constraints/ConstraintsSummaryTile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package explore.constraints

import cats.Order.*
import cats.effect.IO
import cats.syntax.all.*
import crystal.react.*
import explore.Icons
Expand Down Expand Up @@ -111,169 +112,184 @@ object ConstraintsSummaryTile:
) extends ReactFnProps(Body.component)

private object Body:
private type Props = Body

private val ColDef = ColumnDef[ConstraintGroup]

private val ColumnClasses: Map[ColumnId, Css] = Map(
EditColumnId -> (ExploreStyles.StickyColumn |+| ExploreStyles.ConstraintsSummaryEdit)
)

private val component =
ScalaFnComponent
.withHooks[Props]
.useContext(AppContext.ctx)
.useMemoBy((_, _) => ()): // Cols never changes, but needs access to props
(props, ctx) =>
_ =>
def column[V](id: ColumnId, accessor: ConstraintGroup => V)
: ColumnDef.Single.NoMeta[ConstraintGroup, V] =
ColDef(id, accessor, ColumnNames(id))
private def columns(
props: Body,
ctx: AppContext[IO]
): List[ColumnDef.NoMeta[ConstraintGroup, ?]] =
def column[V](
id: ColumnId,
accessor: ConstraintGroup => V
): ColumnDef.Single.NoMeta[ConstraintGroup, V] =
ColDef(id, accessor, ColumnNames(id))

def goToObsSet(obsIdSet: ObsIdSet): Callback =
ctx.pushPage((AppTab.Constraints, props.programId, Focused.obsSet(obsIdSet)).some)
def goToObsSet(obsIdSet: ObsIdSet): Callback =
ctx.pushPage(
(AppTab.Constraints, props.programId, Focused.obsSet(obsIdSet)).some
)

def obsSetUrl(obsIdSet: ObsIdSet): String =
ctx.pageUrl((AppTab.Constraints, props.programId, Focused.obsSet(obsIdSet)).some)
def obsSetUrl(obsIdSet: ObsIdSet): String =
ctx.pageUrl(
(AppTab.Constraints, props.programId, Focused.obsSet(obsIdSet)).some
)

def goToObs(obsId: Observation.Id): Callback =
ctx.pushPage((AppTab.Constraints, props.programId, Focused.singleObs(obsId)).some)
def goToObs(obsId: Observation.Id): Callback =
ctx.pushPage(
(AppTab.Constraints, props.programId, Focused.singleObs(obsId)).some
)

def obsUrl(obsId: Observation.Id): String =
ctx.pageUrl((AppTab.Constraints, props.programId, Focused.singleObs(obsId)).some)
def obsUrl(obsId: Observation.Id): String =
ctx.pageUrl(
(AppTab.Constraints, props.programId, Focused.singleObs(obsId)).some
)

List(
column(EditColumnId, ConstraintGroup.obsIds.get)
.setCell(cell =>
<.a(^.href := obsSetUrl(cell.value),
^.onClick ==> (_.preventDefaultCB *> goToObsSet(cell.value)),
Icons.Edit
)
)
.setEnableSorting(false),
column(
IQColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.imageQuality).get
)
.setCell(_.value.label)
.sortableBy(_.label),
column(
CCColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.cloudExtinction).get
)
.setCell(_.value.label)
.sortableBy(_.label),
column(
BGColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.skyBackground).get
)
.setCell(_.value.label)
.sortableBy(_.label),
column(
WVColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.waterVapor).get
)
.setCell(_.value.label)
.sortableBy(_.label),
column(
MinAMColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.elevationRange).get
)
.setCell(_.value match
case ElevationRange.AirMass(min, _) => f"${min.value}%.1f"
case ElevationRange.HourAngle(_, _) => ""
)
.sortableBy(_ match
case ElevationRange.AirMass(min, _) => min.value
case ElevationRange.HourAngle(_, _) => ElevationRange.AirMass.MinValue - 1
),
column(
MaxAMColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.elevationRange).get
)
.setCell(_.value match
case ElevationRange.AirMass(_, max) => f"${max.value}%.1f"
case ElevationRange.HourAngle(_, _) => ""
)
.sortableBy(_ match
case ElevationRange.AirMass(_, max) => max.value
case ElevationRange.HourAngle(_, _) => ElevationRange.AirMass.MinValue - 1
),
column(
MinHAColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.elevationRange).get
)
.setCell(_.value match
case ElevationRange.AirMass(_, _) => ""
case ElevationRange.HourAngle(min, _) => f"${min.value}%.1f"
List(
column(EditColumnId, ConstraintGroup.obsIds.get)
.setCell(cell =>
<.a(^.href := obsSetUrl(cell.value),
^.onClick ==> (_.preventDefaultCB *> goToObsSet(cell.value)),
Icons.Edit
)
)
.setEnableSorting(false),
column(
IQColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.imageQuality).get
)
.setCell(_.value.label)
.sortableBy(_.label),
column(
CCColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.cloudExtinction).get
)
.setCell(_.value.label)
.sortableBy(_.label),
column(
BGColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.skyBackground).get
)
.setCell(_.value.label)
.sortableBy(_.label),
column(
WVColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.waterVapor).get
)
.setCell(_.value.label)
.sortableBy(_.label),
column(
MinAMColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.elevationRange).get
)
.setCell(_.value match
case ElevationRange.AirMass(min, _) => f"${min.value}%.1f"
case ElevationRange.HourAngle(_, _) => ""
)
.sortableBy(_ match
case ElevationRange.AirMass(min, _) => min.value
case ElevationRange.HourAngle(_, _) =>
ElevationRange.AirMass.MinValue - 1
),
column(
MaxAMColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.elevationRange).get
)
.setCell(_.value match
case ElevationRange.AirMass(_, max) => f"${max.value}%.1f"
case ElevationRange.HourAngle(_, _) => ""
)
.sortableBy(_ match
case ElevationRange.AirMass(_, max) => max.value
case ElevationRange.HourAngle(_, _) =>
ElevationRange.AirMass.MinValue - 1
),
column(
MinHAColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.elevationRange).get
)
.setCell(_.value match
case ElevationRange.AirMass(_, _) => ""
case ElevationRange.HourAngle(min, _) => f"${min.value}%.1f"
)
.sortableBy(_ match
case ElevationRange.AirMass(_, _) =>
ElevationRange.HourAngle.MinHour - 1
case ElevationRange.HourAngle(min, _) => min.value
),
column(
MaxHAColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.elevationRange).get
)
.setCell(_.value match
case ElevationRange.AirMass(_, _) => ""
case ElevationRange.HourAngle(_, max) => f"${max.value}%.1f"
)
.sortableBy(_ match
case ElevationRange.AirMass(_, _) =>
ElevationRange.HourAngle.MinHour - 1
case ElevationRange.HourAngle(_, max) => max.value
),
column(CountColumnId, _.obsIds.length),
column(ObservationsColumnId, ConstraintGroup.obsIds.get)
.setCell(cell =>
<.span(
cell.value.toSortedSet.toList
.map(obsId =>
<.a(
^.href := obsUrl(obsId),
^.onClick ==> (_.preventDefaultCB
>> goToObs(obsId)
>> props.expandedIds.mod(_ + cell.value)
>> goToObsSet(ObsIdSet.one(obsId))),
obsId.toString
)
.sortableBy(_ match
case ElevationRange.AirMass(_, _) => ElevationRange.HourAngle.MinHour - 1
case ElevationRange.HourAngle(min, _) => min.value
),
column(
MaxHAColumnId,
ConstraintGroup.constraintSet.andThen(ConstraintSet.elevationRange).get
)
.setCell(_.value match
case ElevationRange.AirMass(_, _) => ""
case ElevationRange.HourAngle(_, max) => f"${max.value}%.1f"
)
.sortableBy(_ match
case ElevationRange.AirMass(_, _) => ElevationRange.HourAngle.MinHour - 1
case ElevationRange.HourAngle(_, max) => max.value
),
column(CountColumnId, _.obsIds.length),
column(ObservationsColumnId, ConstraintGroup.obsIds.get)
.setCell(cell =>
<.span(
cell.value.toSortedSet.toList
.map(obsId =>
<.a(
^.href := obsUrl(obsId),
^.onClick ==> (_.preventDefaultCB
>> goToObs(obsId)
>> props.expandedIds.mod(_ + cell.value)
>> goToObsSet(ObsIdSet.one(obsId))),
obsId.toString
)
)
.mkReactFragment(", ")
)
)
.setEnableSorting(false)
)
// Memo rows
.useMemoBy((props, _, _) => props.constraintList): (_, _, _) =>
_.map(ConstraintGroup.fromTuple).toList.sortBy(_.constraintSet.summaryString)
.useReactTableWithStateStoreBy: (props, ctx, cols, rows) =>
import ctx.given

TableOptionsWithStateStore(
TableOptions(
cols,
rows,
getRowId = (row, _, _) => RowId(row.constraintSet.toString),
enableSorting = true,
enableColumnResizing = true,
columnResizeMode = ColumnResizeMode.OnChange,
state = PartialTableState(columnVisibility = props.columnVisibility.get),
onColumnVisibilityChange = stateInViewHandler(props.columnVisibility.mod)
),
TableStore(props.userId, TableId.ConstraintsSummary, cols)
)
.render: (props, _, _, _, table) =>
<.div(
PrimeTable(
table,
striped = true,
compact = Compact.Very,
tableMod = ExploreStyles.ExploreTable,
emptyMessage = <.div("No constraints present"),
headerCellMod = headerCell =>
ColumnClasses
.get(headerCell.column.id)
.orEmpty |+| ExploreStyles.StickyHeader,
cellMod = cell => ColumnClasses.get(cell.column.id).orEmpty
.mkReactFragment(", ")
)
)
.setEnableSorting(false)
)

private val component =
ScalaFnComponent[Body]: props =>
for {
ctx <- useContext(AppContext.ctx)
cols <- useMemo(()): // Cols never changes, but needs access to props
_ => columns(props, ctx)
// Memo rows
rows <- useMemo(props.constraintList):
_.map(ConstraintGroup.fromTuple).toList.sortBy(_.constraintSet.summaryString)
table <- useReactTableWithStateStore:
import ctx.given

TableOptionsWithStateStore(
TableOptions(
cols,
rows,
getRowId = (row, _, _) => RowId(row.constraintSet.toString),
enableSorting = true,
enableColumnResizing = true,
columnResizeMode = ColumnResizeMode.OnChange,
state = PartialTableState(columnVisibility = props.columnVisibility.get),
onColumnVisibilityChange = stateInViewHandler(props.columnVisibility.mod)
),
TableStore(props.userId, TableId.ConstraintsSummary, cols)
)
} yield <.div(
PrimeTable(
table,
striped = true,
compact = Compact.Very,
tableMod = ExploreStyles.ExploreTable,
emptyMessage = <.div("No constraints present"),
headerCellMod = headerCell =>
ColumnClasses
.get(headerCell.column.id)
.orEmpty |+| ExploreStyles.StickyHeader,
cellMod = cell => ColumnClasses.get(cell.column.id).orEmpty
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@ case class AsterismGroupObsList(
private val observations: UndoSetter[ObservationList] =
undoCtx.zoom(ProgramSummaries.observations)

private val calibrationObservations: Set[Observation.Id] =
undoCtx.get.calibrationObservations

override val focusedObsSet: Option[ObsIdSet] = focused.obsSet

private val asterismGroups: AsterismGroupList = programSummaries.get.asterismGroups
Expand Down Expand Up @@ -288,11 +285,7 @@ object AsterismGroupObsList:
val asterismGroups: List[AsterismGroup] =
props.programSummaries.get.asterismGroups
.map(AsterismGroup.fromTuple)
.map: asterismGroup =>
(asterismGroup.obsIds -- props.calibrationObservations).map: filteredObsIds =>
AsterismGroup(filteredObsIds, asterismGroup.targetIds)
.toList
.flattenOption

val targetWithObsMap: SortedMap[Target.Id, TargetWithObs] =
props.programSummaries.get.targetsWithObs
Expand Down
Loading
Loading