Skip to content

Commit

Permalink
Updated observation store to change stauts on claim/release/complete (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tommylau523 authored Nov 4, 2024
1 parent a86e686 commit 731d09c
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.terraformation.backend.db.default_schema.SpeciesId
import com.terraformation.backend.db.default_schema.tables.references.USERS
import com.terraformation.backend.db.forMultiset
import com.terraformation.backend.db.tracking.ObservationId
import com.terraformation.backend.db.tracking.ObservationPlotStatus
import com.terraformation.backend.db.tracking.PlantingSiteId
import com.terraformation.backend.db.tracking.RecordedSpeciesCertainty
import com.terraformation.backend.db.tracking.tables.references.MONITORING_PLOTS
Expand Down Expand Up @@ -271,6 +270,7 @@ class ObservationResultsStore(private val dslContext: DSLContext) {
OBSERVATION_PLOTS.COMPLETED_TIME,
OBSERVATION_PLOTS.IS_PERMANENT,
OBSERVATION_PLOTS.NOTES,
OBSERVATION_PLOTS.STATUS_ID,
monitoringPlotsBoundaryField,
MONITORING_PLOTS.ID,
MONITORING_PLOTS.FULL_NAME,
Expand Down Expand Up @@ -309,12 +309,7 @@ class ObservationResultsStore(private val dslContext: DSLContext) {
val plantingDensity =
(totalLive * SQUARE_METERS_PER_HECTARE / areaSquareMeters).roundToInt()

val status =
when {
completedTime != null -> ObservationPlotStatus.Completed
claimedBy != null -> ObservationPlotStatus.Claimed
else -> ObservationPlotStatus.Unclaimed
}
val status = record[OBSERVATION_PLOTS.STATUS_ID]!!

ObservationMonitoringPlotResultsModel(
boundary = record[monitoringPlotsBoundaryField] as Polygon,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,27 +583,33 @@ class ObservationStore(
val rowsUpdated =
dslContext
.update(OBSERVATION_PLOTS)
.set(OBSERVATION_PLOTS.STATUS_ID, ObservationPlotStatus.Claimed)
.set(OBSERVATION_PLOTS.CLAIMED_BY, currentUser().userId)
.set(OBSERVATION_PLOTS.CLAIMED_TIME, clock.instant())
.where(OBSERVATION_PLOTS.OBSERVATION_ID.eq(observationId))
.and(OBSERVATION_PLOTS.MONITORING_PLOT_ID.eq(monitoringPlotId))
.and(
OBSERVATION_PLOTS.CLAIMED_BY.isNull.or(
OBSERVATION_PLOTS.CLAIMED_BY.eq(currentUser().userId)))
OBSERVATION_PLOTS.STATUS_ID.eq(ObservationPlotStatus.Unclaimed)
.or(
OBSERVATION_PLOTS.STATUS_ID.eq(ObservationPlotStatus.Claimed)
.and(OBSERVATION_PLOTS.CLAIMED_BY.eq(currentUser().userId))))
.execute()

if (rowsUpdated == 0) {
val plotAssignment =
val plotStatus =
dslContext
.selectOne()
.select(OBSERVATION_PLOTS.STATUS_ID)
.from(OBSERVATION_PLOTS)
.where(OBSERVATION_PLOTS.OBSERVATION_ID.eq(observationId))
.and(OBSERVATION_PLOTS.MONITORING_PLOT_ID.eq(monitoringPlotId))
.fetch()
if (plotAssignment.isEmpty()) {
.fetch { it[OBSERVATION_PLOTS.STATUS_ID]!! }
if (plotStatus.isEmpty()) {
throw PlotNotInObservationException(observationId, monitoringPlotId)
} else {
throw PlotAlreadyClaimedException(monitoringPlotId)
when (plotStatus.first()) {
ObservationPlotStatus.Claimed -> throw PlotAlreadyClaimedException(monitoringPlotId)
else -> throw PlotAlreadyCompletedException(monitoringPlotId)
}
}
}
}
Expand All @@ -614,27 +620,31 @@ class ObservationStore(
val rowsUpdated =
dslContext
.update(OBSERVATION_PLOTS)
.set(OBSERVATION_PLOTS.STATUS_ID, ObservationPlotStatus.Unclaimed)
.setNull(OBSERVATION_PLOTS.CLAIMED_BY)
.setNull(OBSERVATION_PLOTS.CLAIMED_TIME)
.where(OBSERVATION_PLOTS.OBSERVATION_ID.eq(observationId))
.and(OBSERVATION_PLOTS.MONITORING_PLOT_ID.eq(monitoringPlotId))
.and(OBSERVATION_PLOTS.STATUS_ID.eq(ObservationPlotStatus.Claimed))
.and(OBSERVATION_PLOTS.CLAIMED_BY.eq(currentUser().userId))
.execute()

if (rowsUpdated == 0) {
val plotClaim =
val plotStatus =
dslContext
.select(OBSERVATION_PLOTS.CLAIMED_BY)
.select(OBSERVATION_PLOTS.STATUS_ID)
.from(OBSERVATION_PLOTS)
.where(OBSERVATION_PLOTS.OBSERVATION_ID.eq(observationId))
.and(OBSERVATION_PLOTS.MONITORING_PLOT_ID.eq(monitoringPlotId))
.fetch(OBSERVATION_PLOTS.CLAIMED_BY)
if (plotClaim.isEmpty()) {
.fetch { it[OBSERVATION_PLOTS.STATUS_ID]!! }
if (plotStatus.isEmpty()) {
throw PlotNotInObservationException(observationId, monitoringPlotId)
} else if (plotClaim.first() == null) {
throw PlotNotClaimedException(monitoringPlotId)
} else {
throw PlotAlreadyClaimedException(monitoringPlotId)
when (plotStatus.first()) {
ObservationPlotStatus.Unclaimed -> throw PlotNotClaimedException(monitoringPlotId)
ObservationPlotStatus.Claimed -> throw PlotAlreadyClaimedException(monitoringPlotId)
else -> throw PlotAlreadyCompletedException(monitoringPlotId)
}
}
}
}
Expand Down Expand Up @@ -676,7 +686,8 @@ class ObservationStore(
.fetchOneInto(ObservationPlotsRow::class.java)
?: throw PlotNotInObservationException(observationId, monitoringPlotId)

if (observationPlotsRow.completedTime != null) {
if (observationPlotsRow.statusId == ObservationPlotStatus.Completed ||
observationPlotsRow.statusId == ObservationPlotStatus.NotObserved) {
throw PlotAlreadyCompletedException(monitoringPlotId)
}

Expand Down Expand Up @@ -710,7 +721,9 @@ class ObservationStore(
completedBy = currentUser().userId,
completedTime = clock.instant(),
notes = notes,
observedTime = observedTime))
observedTime = observedTime,
statusId = ObservationPlotStatus.Completed,
))

val allPlotsCompleted =
dslContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.terraformation.backend.db.tracking.MonitoringPlotId
import com.terraformation.backend.db.tracking.ObservableCondition
import com.terraformation.backend.db.tracking.ObservationId
import com.terraformation.backend.db.tracking.ObservationPlotPosition
import com.terraformation.backend.db.tracking.ObservationPlotStatus
import com.terraformation.backend.db.tracking.ObservationState
import com.terraformation.backend.db.tracking.PlantingSiteId
import com.terraformation.backend.db.tracking.RecordedPlantStatus.Dead
Expand Down Expand Up @@ -172,7 +173,11 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser {
val monitoringPlotId12 =
insertMonitoringPlot(boundary = polygon(2), fullName = "Z1-S1-2", name = "2")
val claimedTime12 = Instant.ofEpochSecond(12)
insertObservationPlot(ObservationPlotsRow(claimedBy = userId1, claimedTime = claimedTime12))
insertObservationPlot(
ObservationPlotsRow(
claimedBy = userId1,
claimedTime = claimedTime12,
statusId = ObservationPlotStatus.Claimed))

val plantingSubzoneId2 = insertPlantingSubzone(fullName = "Z1-S2", name = "S2")

Expand All @@ -189,7 +194,9 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser {
completedBy = userId1,
completedTime = completedTime21,
notes = "Some notes",
observedTime = observedTime21))
observedTime = observedTime21,
statusId = ObservationPlotStatus.Completed,
))

val expected =
listOf(
Expand Down Expand Up @@ -854,20 +861,25 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser {

val row = observationPlotsDao.findAll().first()

assertEquals(ObservationPlotStatus.Claimed, row.statusId, "Plot status")
assertEquals(user.userId, row.claimedBy, "Claimed by")
assertEquals(clock.instant, row.claimedTime, "Claimed time")
}

@Test
fun `updates claim time if plot is reclaimed by current claimant`() {
insertObservationPlot(claimedBy = user.userId, claimedTime = Instant.EPOCH)
insertObservationPlot(
claimedBy = user.userId,
claimedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.Claimed)

clock.instant = Instant.ofEpochSecond(2)

store.claimPlot(observationId, plotId)

val plotsRow = observationPlotsDao.findAll().first()

assertEquals(ObservationPlotStatus.Claimed, plotsRow.statusId, "Plot status is unchanged")
assertEquals(user.userId, plotsRow.claimedBy, "Should remain claimed by user")
assertEquals(clock.instant, plotsRow.claimedTime, "Claim time should be updated")
}
Expand All @@ -876,11 +888,34 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser {
fun `throws exception if plot is claimed by someone else`() {
val otherUserId = insertUser()

insertObservationPlot(claimedBy = otherUserId, claimedTime = Instant.EPOCH)
insertObservationPlot(
claimedBy = otherUserId,
claimedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.Claimed)

assertThrows<PlotAlreadyClaimedException> { store.claimPlot(observationId, plotId) }
}

@Test
fun `throws exception if plot observation status is completed`() {
insertObservationPlot(
claimedBy = user.userId,
claimedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.Completed)

assertThrows<PlotAlreadyCompletedException> { store.claimPlot(observationId, plotId) }
}

@Test
fun `throws exception if plot observation status is not observed`() {
insertObservationPlot(
claimedBy = user.userId,
claimedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.NotObserved)

assertThrows<PlotAlreadyCompletedException> { store.claimPlot(observationId, plotId) }
}

@Test
fun `throws exception if no permission to update observation`() {
insertObservationPlot()
Expand Down Expand Up @@ -911,12 +946,16 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser {

@Test
fun `releases claim on plot`() {
insertObservationPlot(claimedBy = user.userId, claimedTime = Instant.EPOCH)
insertObservationPlot(
claimedBy = user.userId,
claimedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.Claimed)

store.releasePlot(observationId, plotId)

val row = observationPlotsDao.findAll().first()

assertEquals(ObservationPlotStatus.Unclaimed, row.statusId, "Plot status")
assertNull(row.claimedBy, "Claimed by")
assertNull(row.claimedTime, "Claimed time")
}
Expand All @@ -932,11 +971,34 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser {
fun `throws exception if plot is claimed by someone else`() {
val otherUserId = insertUser()

insertObservationPlot(claimedBy = otherUserId, claimedTime = Instant.EPOCH)
insertObservationPlot(
claimedBy = otherUserId,
claimedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.Claimed)

assertThrows<PlotAlreadyClaimedException> { store.releasePlot(observationId, plotId) }
}

@Test
fun `throws exception if plot observation status is completed`() {
insertObservationPlot(
claimedBy = user.userId,
claimedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.Completed)

assertThrows<PlotAlreadyCompletedException> { store.releasePlot(observationId, plotId) }
}

@Test
fun `throws exception if plot observation status is not observed`() {
insertObservationPlot(
claimedBy = user.userId,
claimedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.NotObserved)

assertThrows<PlotAlreadyCompletedException> { store.releasePlot(observationId, plotId) }
}

@Test
fun `throws exception if no permission to update observation`() {
insertObservationPlot()
Expand Down Expand Up @@ -1029,7 +1091,9 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser {
completedBy = user.userId,
completedTime = clock.instant,
notes = "Notes",
observedTime = observedTime)
observedTime = observedTime,
statusId = ObservationPlotStatus.Completed,
)
} else {
row
}
Expand Down Expand Up @@ -1478,7 +1542,9 @@ class ObservationStoreTest : DatabaseTest(), RunsAsUser {
claimedTime = Instant.EPOCH,
completedBy = user.userId,
completedTime = Instant.EPOCH,
observedTime = Instant.EPOCH))
observedTime = Instant.EPOCH,
statusId = ObservationPlotStatus.Completed,
))

assertThrows<PlotAlreadyCompletedException> {
store.completePlot(observationId, plotId, emptySet(), null, Instant.EPOCH, emptyList())
Expand Down

0 comments on commit 731d09c

Please sign in to comment.