Skip to content

Commit

Permalink
add global column to challenges and implement filtering for the column
Browse files Browse the repository at this point in the history
  • Loading branch information
CollinBeczak committed Jul 15, 2024
1 parent 5949dc0 commit 2ec2d35
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 8 deletions.
24 changes: 24 additions & 0 deletions app/org/maproulette/framework/mixins/SearchParametersMixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ trait SearchParametersMixin {
var filterList = List(
this.filterLocation(params),
this.filterBounding(params),
this.filterChallengeGlobal(params),
this.filterTaskStatus(params),
this.filterTaskId(params),
this.filterTaskFeatureId(params),
Expand Down Expand Up @@ -717,6 +718,29 @@ trait SearchParametersMixin {
}
}

/**
* Filters by c.is_global. Will only include if
* challengeParams.filterGlobal value is true
*/
def filterChallengeGlobal(params: SearchParameters): FilterGroup = {
params.challengeParams.filterGlobal match {
case Some(v) if v =>
FilterGroup(
List(
FilterParameter.conditional(
"is_global",
value = "false",
Operator.EQ,
useValueDirectly = true,
includeOnlyIfTrue = true,
table = Some("c")
)
)
)
case _ => FilterGroup(List())
}
}

/**
* Filters by c.requires_local
*/
Expand Down
2 changes: 2 additions & 0 deletions app/org/maproulette/framework/model/Challenge.scala
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ case class Challenge(
override val modified: DateTime,
override val description: Option[String] = None,
deleted: Boolean = false,
isGlobal: Boolean = false,
infoLink: Option[String] = None,
general: ChallengeGeneral,
creation: ChallengeCreation,
Expand Down Expand Up @@ -328,6 +329,7 @@ object Challenge extends CommonField {
DateTime.now(),
None,
false,
false,
None,
ChallengeGeneral(-1, -1, ""),
ChallengeCreation(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ object ChallengeRepository {
get[Option[String]]("locationJSON") ~
get[Option[String]]("boundingJSON") ~
get[Boolean]("deleted") ~
get[Boolean]("is_global") ~
get[Option[List[Long]]]("virtual_parent_ids") ~
get[Boolean]("challenges.is_archived") ~
get[Int]("challenges.review_setting") ~
Expand All @@ -272,7 +273,7 @@ object ChallengeRepository {
mediumPriorityRule ~ lowPriorityRule ~ defaultZoom ~ minZoom ~ maxZoom ~ defaultBasemap ~ defaultBasemapId ~
customBasemap ~ updateTasks ~ exportableProperties ~ osmIdProperty ~ taskBundleIdProperty ~ preferredTags ~ preferredReviewTags ~
limitTags ~ limitReviewTags ~ taskStyles ~ lastTaskRefresh ~ dataOriginDate ~ requiresLocal ~ location ~ bounding ~
deleted ~ virtualParents ~ isArchived ~ reviewSetting ~ taskWidgetLayout ~ systemArchivedAt =>
deleted ~ isGlobal ~ virtualParents ~ isArchived ~ reviewSetting ~ taskWidgetLayout ~ systemArchivedAt =>
val hpr = highPriorityRule match {
case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None
case r => r
Expand All @@ -293,6 +294,7 @@ object ChallengeRepository {
modified,
description,
deleted,
isGlobal,
infoLink,
ChallengeGeneral(
ownerId,
Expand Down
63 changes: 58 additions & 5 deletions app/org/maproulette/models/dal/ChallengeDAL.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class ChallengeDAL @Inject() (
get[Option[String]]("boundingJSON") ~
get[Boolean]("challenges.requires_local") ~
get[Boolean]("deleted") ~
get[Boolean]("is_global") ~
get[Boolean]("challenges.is_archived") ~
get[Int]("challenges.review_setting") ~
get[Option[JsValue]]("challenges.task_widget_layout") ~
Expand All @@ -132,7 +133,7 @@ class ChallengeDAL @Inject() (
minZoom ~ maxZoom ~ defaultBasemap ~ defaultBasemapId ~ customBasemap ~ updateTasks ~
exportableProperties ~ osmIdProperty ~ taskBundleIdProperty ~ preferredTags ~ preferredReviewTags ~
limitTags ~ limitReviewTags ~ taskStyles ~ lastTaskRefresh ~ dataOriginDate ~ location ~ bounding ~
requiresLocal ~ deleted ~ isArchived ~ reviewSetting ~ taskWidgetLayout ~ completionPercentage ~ tasksRemaining =>
requiresLocal ~ deleted ~ isGlobal ~ isArchived ~ reviewSetting ~ taskWidgetLayout ~ completionPercentage ~ tasksRemaining =>
val hpr = highPriorityRule match {
case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None
case r => r
Expand All @@ -153,6 +154,7 @@ class ChallengeDAL @Inject() (
modified,
description,
deleted,
isGlobal,
infoLink,
ChallengeGeneral(
ownerId,
Expand Down Expand Up @@ -255,6 +257,7 @@ class ChallengeDAL @Inject() (
get[Option[String]]("boundingJSON") ~
get[Boolean]("challenges.requires_local") ~
get[Boolean]("deleted") ~
get[Boolean]("is_global") ~
get[Option[List[Long]]]("virtual_parent_ids") ~
get[Option[List[String]]]("presets") ~
get[Boolean]("challenges.is_archived") ~
Expand All @@ -270,7 +273,7 @@ class ChallengeDAL @Inject() (
lowPriorityRule ~ defaultZoom ~ minZoom ~ maxZoom ~ defaultBasemap ~ defaultBasemapId ~
customBasemap ~ updateTasks ~ exportableProperties ~ osmIdProperty ~ taskBundleIdProperty ~ preferredTags ~
preferredReviewTags ~ limitTags ~ limitReviewTags ~ taskStyles ~ lastTaskRefresh ~
dataOriginDate ~ location ~ bounding ~ requiresLocal ~ deleted ~ virtualParents ~
dataOriginDate ~ location ~ bounding ~ requiresLocal ~ deleted ~ isGlobal ~ virtualParents ~
presets ~ isArchived ~ reviewSetting ~ taskWidgetLayout ~ systemArchivedAt ~ completionPercentage ~ tasksRemaining =>
val hpr = highPriorityRule match {
case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None
Expand All @@ -292,6 +295,7 @@ class ChallengeDAL @Inject() (
modified,
description,
deleted,
isGlobal,
infoLink,
ChallengeGeneral(
ownerId,
Expand Down Expand Up @@ -475,15 +479,15 @@ class ChallengeDAL @Inject() (
this.cacheManager.withOptionCaching { () =>
val insertedChallenge =
this.withMRTransaction { implicit c =>
SQL"""INSERT INTO challenges (name, owner_id, parent_id, difficulty, description, info_link, blurb,
SQL"""INSERT INTO challenges (name, owner_id, parent_id, difficulty, description, is_global, info_link, blurb,
instruction, enabled, featured, checkin_comment, checkin_source,
overpass_ql, remote_geo_json, overpass_target_type, status, status_message, default_priority, high_priority_rule,
medium_priority_rule, low_priority_rule, default_zoom, min_zoom, max_zoom,
default_basemap, default_basemap_id, custom_basemap, updatetasks, exportable_properties,
osm_id_property, task_bundle_id_property, last_task_refresh, data_origin_date, preferred_tags, preferred_review_tags,
limit_tags, limit_review_tags, task_styles, requires_local, is_archived, review_setting, task_widget_layout)
VALUES (${challenge.name}, ${challenge.general.owner}, ${challenge.general.parent}, ${challenge.general.difficulty},
${challenge.description}, ${challenge.infoLink}, ${challenge.general.blurb}, ${challenge.general.instruction},
${challenge.description}, ${challenge.isGlobal}, ${challenge.infoLink}, ${challenge.general.blurb}, ${challenge.general.instruction},
${challenge.general.enabled}, ${challenge.general.featured},
${challenge.general.checkinComment}, ${challenge.general.checkinSource}, ${challenge.creation.overpassQL}, ${challenge.creation.remoteGeoJson},
${challenge.creation.overpassTargetType}, ${challenge.status},
Expand Down Expand Up @@ -520,6 +524,44 @@ class ChallengeDAL @Inject() (
}
}

def updateSpatialFields(user: User)(implicit challengeId: Long, c: Option[Connection] = None) = {
this.withMRConnection { implicit c =>
// Update location, bounding, and last_updated based on tasks
val updateLocationQuery =
s"""
UPDATE challenges SET
location = (
SELECT ST_Centroid(ST_Collect(ST_Makevalid(location)))
FROM tasks
WHERE parent_id = $challengeId
),
bounding = (
SELECT ST_Envelope(ST_Buffer((ST_SetSRID(ST_Extent(location), 4326))::geography, 2)::geometry)
FROM tasks
WHERE parent_id = $challengeId
),
last_updated = NOW()
WHERE id = $challengeId;
""".stripMargin
SQL(updateLocationQuery).executeUpdate()

// Update is_global based on bounding box dimensions
val updateIsGlobalQuery =
s"""
UPDATE challenges
SET is_global = (
CASE
WHEN (ST_XMax(bounding)::numeric - ST_XMin(bounding)::numeric) > 180 THEN TRUE
WHEN (ST_YMax(bounding)::numeric - ST_YMin(bounding)::numeric) > 90 THEN TRUE
ELSE FALSE
END
)
WHERE id = $challengeId;
""".stripMargin
SQL(updateIsGlobalQuery).executeUpdate()
}
}

private def insertPresets(challenge: Challenge, presets: List[String])(
implicit c: Option[Connection] = None
): Challenge = {
Expand Down Expand Up @@ -580,6 +622,7 @@ class ChallengeDAL @Inject() (
val parentId = (updates \ "parentId").asOpt[Long].getOrElse(cachedItem.general.parent)
val difficulty =
(updates \ "difficulty").asOpt[Int].getOrElse(cachedItem.general.difficulty)
val isGlobal = (updates \ "isGlobal").asOpt[Boolean].getOrElse(cachedItem.isGlobal)
val description =
(updates \ "description").asOpt[String].getOrElse(cachedItem.description.getOrElse(""))
val infoLink =
Expand Down Expand Up @@ -692,7 +735,7 @@ class ChallengeDAL @Inject() (
.getOrElse(cachedItem.extra.presets.getOrElse(null))

val updatedChallenge =
SQL"""UPDATE challenges SET name = $name, owner_id = $ownerId, parent_id = $parentId, difficulty = $difficulty,
SQL"""UPDATE challenges SET name = $name, owner_id = $ownerId, parent_id = $parentId, difficulty = $difficulty, is_global = $isGlobal,
description = $description, info_link = $infoLink, blurb = $blurb, instruction = $instruction,
enabled = $enabled, featured = $featured, checkin_comment = $checkinComment, checkin_source = $checkinSource, overpass_ql = $overpassQL,
remote_geo_json = $remoteGeoJson, overpass_target_type = $overpassTargetType, status = $status, status_message = $statusMessage, default_priority = $defaultPriority,
Expand Down Expand Up @@ -1811,6 +1854,10 @@ class ChallengeDAL @Inject() (
case None =>
}

if (searchParameters.challengeParams.filterGlobal == true) {
this.appendInWhereClause(whereClause, s"c.is_global = false")
}

searchParameters.projectEnabled match {
case Some(true) => this.appendInWhereClause(whereClause, this.enabled(true, "p")(None))
case _ =>
Expand Down Expand Up @@ -1843,6 +1890,12 @@ class ChallengeDAL @Inject() (
this.appendInWhereClause(whereClause, s"c.is_archived = false")
}

searchParameters.challengeParams.filterGlobal match {
case Some(v) if v =>
this.appendInWhereClause(whereClause, s"c.is_global = false")
case _ =>
}

searchParameters.challengeParams.requiresLocal match {
case Some(SearchParameters.CHALLENGE_REQUIRES_LOCAL_EXCLUDE) =>
this.appendInWhereClause(whereClause, s"c.requires_local = false")
Expand Down
2 changes: 2 additions & 0 deletions app/org/maproulette/models/utils/ChallengeFormatters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ trait ChallengeWrites extends DefaultWrites {
(JsPath \ "modified").write[DateTime] and
(JsPath \ "description").writeNullable[String] and
(JsPath \ "deleted").write[Boolean] and
(JsPath \ "isGlobal").write[Boolean] and
(JsPath \ "infoLink").writeNullable[String] and
JsPath.write[ChallengeGeneral] and
JsPath.write[ChallengeCreation] and
Expand Down Expand Up @@ -110,6 +111,7 @@ trait ChallengeReads extends DefaultReads {
((JsPath \ "modified").read[DateTime] or Reads.pure(DateTime.now())) and
(JsPath \ "description").readNullable[String] and
(JsPath \ "deleted").read[Boolean] and
(JsPath \ "isGlobal").read[Boolean] and
(JsPath \ "infoLink").readNullable[String] and
JsPath.read[ChallengeGeneral] and
JsPath.read[ChallengeCreation] and
Expand Down
8 changes: 8 additions & 0 deletions app/org/maproulette/provider/ChallengeProvider.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class ChallengeProvider @Inject() (
user
)(challenge.id)
} else {
this.challengeDAL.updateSpatialFields(user)(challenge.id)
this.challengeDAL.update(Json.obj("status" -> Challenge.STATUS_READY), user)(
challenge.id
)
Expand Down Expand Up @@ -242,6 +243,9 @@ class ChallengeProvider @Inject() (
jsonData
)
}
this.challengeDAL.updateSpatialFields(user)(
challenge.id
)
this.challengeDAL.update(Json.obj("status" -> Challenge.STATUS_READY), user)(
challenge.id
)
Expand All @@ -261,6 +265,7 @@ class ChallengeProvider @Inject() (
if (seqJSON) {
this.buildTasksFromRemoteJson(filePrefix, fileNumber + 1, challenge, user, false)
} else {
this.challengeDAL.updateSpatialFields(user)(challenge.id)
this.challengeDAL.update(Json.obj("status" -> Challenge.STATUS_READY), user)(challenge.id)
this.challengeDAL.markTasksRefreshed()(challenge.id)

Expand All @@ -272,6 +277,7 @@ class ChallengeProvider @Inject() (
case Failure(f) =>
if (fileNumber > 1) {
// todo need to figure out if actual failure or if not finding the next file
this.challengeDAL.updateSpatialFields(user)(challenge.id)
this.challengeDAL.update(Json.obj("status" -> Challenge.STATUS_READY), user)(challenge.id)
} else {
this.challengeDAL.update(
Expand Down Expand Up @@ -419,6 +425,7 @@ class ChallengeProvider @Inject() (
}
}

this.challengeDAL.updateSpatialFields(user)(parent.id)
this.challengeDAL.update(Json.obj("status" -> Challenge.STATUS_READY), user)(parent.id)
this.challengeDAL.markTasksRefreshed()(parent.id)
if (single) {
Expand Down Expand Up @@ -621,6 +628,7 @@ class ChallengeProvider @Inject() (
user
)(challenge.id)
case false =>
this.challengeDAL.updateSpatialFields(user)(challenge.id)
this.challengeDAL.update(Json.obj("status" -> Challenge.STATUS_READY), user)(
challenge.id
)
Expand Down
1 change: 1 addition & 0 deletions app/org/maproulette/provider/KeepRightProvider.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ class KeepRightProvider @Inject() (
DateTime.now(),
None,
false,
false,
None,
ChallengeGeneral(User.superUser.id, rootProjectId, ""),
ChallengeCreation(),
Expand Down
7 changes: 5 additions & 2 deletions app/org/maproulette/session/SearchParameters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ case class SearchChallengeParameters(
challengeDifficulty: Option[Int] = None,
challengeStatus: Option[List[Int]] = None,
requiresLocal: Option[Int] = Some(SearchParameters.CHALLENGE_REQUIRES_LOCAL_EXCLUDE),
archived: Option[Boolean] = None
archived: Option[Boolean] = None,
filterGlobal: Option[Boolean] = None
)

case class SearchReviewParameters(
Expand Down Expand Up @@ -439,7 +440,9 @@ object SearchParameters {
//requiresLocal
this.getIntParameter(request.getQueryString("cLocal"), Some(params.challengeParams.requiresLocal.getOrElse(SearchParameters.CHALLENGE_REQUIRES_LOCAL_EXCLUDE))),
//includeArchived
this.getBooleanParameter(request.getQueryString("ca"), params.challengeParams.archived)
this.getBooleanParameter(request.getQueryString("ca"), params.challengeParams.archived),
//filterGlobal
this.getBooleanParameter(request.getQueryString("fg"), params.challengeParams.filterGlobal)
),
new SearchTaskParameters(
//taskTags
Expand Down
18 changes: 18 additions & 0 deletions conf/evolutions/default/95.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- SQL migration script to add 'is_global' column to 'challenges' table
-- !Ups
-- Add a new column 'is_global' to challenges table if it doesn't exist
ALTER TABLE IF EXISTS challenges ADD COLUMN IF NOT EXISTS is_global BOOLEAN;

-- Update 'is_global' column based on bounding box dimensions
UPDATE challenges
SET is_global = (
CASE
WHEN (ST_XMax(bounding)::numeric - ST_XMin(bounding)::numeric) > 180 THEN TRUE
WHEN (ST_YMax(bounding)::numeric - ST_YMin(bounding)::numeric) > 90 THEN TRUE
ELSE FALSE
END
);

-- !Downs
ALTER TABLE IF EXISTS challenges
DROP COLUMN is_global;
1 change: 1 addition & 0 deletions test/org/maproulette/utils/TestSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ trait TestSpec extends PlaySpec with MockitoSugar {
DateTime.now(),
None,
false,
false,
None,
ChallengeGeneral(101, 1, ""),
ChallengeCreation(),
Expand Down

0 comments on commit 2ec2d35

Please sign in to comment.