From 15dcd50efafdc00491aa32698b2b17f69c28260e Mon Sep 17 00:00:00 2001 From: CollinBeczak Date: Tue, 3 Oct 2023 00:21:38 -0500 Subject: [PATCH 1/6] add stringified layout JSON to challenge table --- .../controllers/api/ChallengeController.scala | 1 + .../framework/model/Challenge.scala | 1 + .../repository/ChallengeRepository.scala | 4 +++- .../maproulette/models/dal/ChallengeDAL.scala | 20 +++++++++++++------ conf/evolutions/default/93.sql | 7 +++++++ 5 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 conf/evolutions/default/93.sql diff --git a/app/org/maproulette/controllers/api/ChallengeController.scala b/app/org/maproulette/controllers/api/ChallengeController.scala index 0db55358..b03ba525 100644 --- a/app/org/maproulette/controllers/api/ChallengeController.scala +++ b/app/org/maproulette/controllers/api/ChallengeController.scala @@ -1167,6 +1167,7 @@ class ChallengeController @Inject() ( Utils.insertIntoJson(jsonBody, "reviewSetting", Challenge.REVIEW_SETTING_NOT_REQUIRED)( IntWrites ) + jsonBody = Utils.insertIntoJson(jsonBody, "layoutJSON", "")(StringWrites) jsonBody = Utils.insertIntoJson(jsonBody, "updateTasks", false)(BooleanWrites) jsonBody = Utils.insertIntoJson(jsonBody, "changesetUrl", false)(BooleanWrites) // if we can't find the parent ID, just use the user's default project instead diff --git a/app/org/maproulette/framework/model/Challenge.scala b/app/org/maproulette/framework/model/Challenge.scala index 139abb7d..34aa391f 100644 --- a/app/org/maproulette/framework/model/Challenge.scala +++ b/app/org/maproulette/framework/model/Challenge.scala @@ -135,6 +135,7 @@ case class ChallengeExtra( taskBundleIdProperty: Option[String] = None, isArchived: Boolean = false, reviewSetting: Int = Challenge.REVIEW_SETTING_NOT_REQUIRED, + layoutJSON: Option[String] = None, systemArchivedAt: Option[DateTime] = None, presets: Option[List[String]] = None ) extends DefaultWrites diff --git a/app/org/maproulette/framework/repository/ChallengeRepository.scala b/app/org/maproulette/framework/repository/ChallengeRepository.scala index 07b0092c..7d0b940c 100644 --- a/app/org/maproulette/framework/repository/ChallengeRepository.scala +++ b/app/org/maproulette/framework/repository/ChallengeRepository.scala @@ -262,6 +262,7 @@ object ChallengeRepository { get[Option[List[Long]]]("virtual_parent_ids") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ + get[Option[String]]("challenges.layout_json") ~ get[Option[DateTime]]("challenges.system_archived_at") map { case id ~ name ~ created ~ modified ~ description ~ infoLink ~ ownerId ~ parentId ~ instruction ~ difficulty ~ blurb ~ enabled ~ featured ~ cooperativeType ~ popularity ~ checkin_comment ~ @@ -269,7 +270,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 ~ systemArchivedAt => + deleted ~ virtualParents ~ isArchived ~ reviewSetting ~ layoutJSON ~ systemArchivedAt => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -325,6 +326,7 @@ object ChallengeRepository { taskBundleIdProperty, isArchived, reviewSetting, + layoutJSON, systemArchivedAt ), status, diff --git a/app/org/maproulette/models/dal/ChallengeDAL.scala b/app/org/maproulette/models/dal/ChallengeDAL.scala index a3b8fecb..b105572c 100644 --- a/app/org/maproulette/models/dal/ChallengeDAL.scala +++ b/app/org/maproulette/models/dal/ChallengeDAL.scala @@ -121,6 +121,7 @@ class ChallengeDAL @Inject() ( get[Boolean]("deleted") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ + get[Option[String]]("challenges.layout_json") ~ get[Option[Int]]("challenges.completion_percentage") ~ get[Option[Int]]("challenges.tasks_remaining") map { case id ~ name ~ created ~ modified ~ description ~ infoLink ~ ownerId ~ parentId ~ instruction ~ @@ -130,7 +131,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 ~ completionPercentage ~ tasksRemaining => + requiresLocal ~ deleted ~ isArchived ~ reviewSetting ~ layoutJSON ~ completionPercentage ~ tasksRemaining => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -185,7 +186,8 @@ class ChallengeDAL @Inject() ( taskStyles, taskBundleIdProperty, isArchived, - reviewSetting + reviewSetting, + layoutJSON ), status, statusMessage, @@ -255,6 +257,7 @@ class ChallengeDAL @Inject() ( get[Option[List[String]]]("presets") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ + get[Option[String]]("challenges.layout_json") ~ get[Option[DateTime]]("challenges.system_archived_at") ~ get[Option[Int]]("challenges.completion_percentage") ~ get[Option[Int]]("challenges.tasks_remaining") map { @@ -266,7 +269,7 @@ class ChallengeDAL @Inject() ( customBasemap ~ updateTasks ~ exportableProperties ~ osmIdProperty ~ taskBundleIdProperty ~ preferredTags ~ preferredReviewTags ~ limitTags ~ limitReviewTags ~ taskStyles ~ lastTaskRefresh ~ dataOriginDate ~ location ~ bounding ~ requiresLocal ~ deleted ~ virtualParents ~ - presets ~ isArchived ~ reviewSetting ~ systemArchivedAt ~ completionPercentage ~ tasksRemaining => + presets ~ isArchived ~ reviewSetting ~ layoutJSON ~ systemArchivedAt ~ completionPercentage ~ tasksRemaining => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -322,6 +325,7 @@ class ChallengeDAL @Inject() ( taskBundleIdProperty, isArchived, reviewSetting, + layoutJSON, systemArchivedAt, presets ), @@ -474,7 +478,7 @@ class ChallengeDAL @Inject() ( 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) + limit_tags, limit_review_tags, task_styles, requires_local, is_archived, review_setting, layout_json) VALUES (${challenge.name}, ${challenge.general.owner}, ${challenge.general.parent}, ${challenge.general.difficulty}, ${challenge.description}, ${challenge.infoLink}, ${challenge.general.blurb}, ${challenge.general.instruction}, ${challenge.general.enabled}, ${challenge.general.featured}, @@ -488,7 +492,7 @@ class ChallengeDAL @Inject() ( ${challenge.dataOriginDate.getOrElse(DateTime.now()).toString}::timestamptz, ${challenge.extra.preferredTags}, ${challenge.extra.preferredReviewTags}, ${challenge.extra.limitTags}, ${challenge.extra.limitReviewTags}, ${challenge.extra.taskStyles}, ${challenge.general.requiresLocal}, ${challenge.extra.isArchived}, - ${challenge.extra.reviewSetting}) + ${challenge.extra.reviewSetting}, ${challenge.extra.layoutJSON}) ON CONFLICT(parent_id, LOWER(name)) DO NOTHING RETURNING #${this.retrieveColumns}""" .as(this.parser.*) .headOption @@ -675,6 +679,10 @@ class ChallengeDAL @Inject() ( .asOpt[Int] .getOrElse(cachedItem.extra.reviewSetting) + val layoutJSON = (updates \ "layoutJSON") + .asOpt[String] + .getOrElse(cachedItem.extra.layoutJSON.getOrElse("")) + val presets: List[String] = (updates \ "presets") .asOpt[List[String]] .getOrElse(cachedItem.extra.presets.getOrElse(null)) @@ -704,7 +712,7 @@ class ChallengeDAL @Inject() ( custom_basemap = $customBasemap, updatetasks = $updateTasks, exportable_properties = $exportableProperties, osm_id_property = $osmIdProperty, task_bundle_id_property = $taskBundleIdProperty, preferred_tags = $preferredTags, preferred_review_tags = $preferredReviewTags, limit_tags = $limitTags, limit_review_tags = $limitReviewTags, task_styles = $taskStyles, - requires_local = $requiresLocal, is_archived = $isArchived, review_setting = $reviewSetting + requires_local = $requiresLocal, is_archived = $isArchived, review_setting = $reviewSetting, layout_json = $layoutJSON WHERE id = $id RETURNING #${this.retrieveColumns}""".as(parser.*).headOption updatedChallenge match { diff --git a/conf/evolutions/default/93.sql b/conf/evolutions/default/93.sql new file mode 100644 index 00000000..4842113f --- /dev/null +++ b/conf/evolutions/default/93.sql @@ -0,0 +1,7 @@ +-- --- !Ups +ALTER TABLE IF EXISTS challenges +ADD COLUMN layout_json text DEFAULT ''; + +-- --- !Downs +ALTER TABLE IF EXISTS challenges +DROP COLUMN layout_json; \ No newline at end of file From d10f57a9434a846bd5a00187786fd1944b238372 Mon Sep 17 00:00:00 2001 From: CollinBeczak Date: Tue, 3 Oct 2023 12:39:29 -0500 Subject: [PATCH 2/6] change variable name --- .../controllers/api/ChallengeController.scala | 2 +- .../framework/model/Challenge.scala | 2 +- .../repository/ChallengeRepository.scala | 6 ++--- .../maproulette/models/dal/ChallengeDAL.scala | 22 +++++++++---------- conf/evolutions/default/93.sql | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/org/maproulette/controllers/api/ChallengeController.scala b/app/org/maproulette/controllers/api/ChallengeController.scala index b03ba525..e9f663e8 100644 --- a/app/org/maproulette/controllers/api/ChallengeController.scala +++ b/app/org/maproulette/controllers/api/ChallengeController.scala @@ -1167,7 +1167,7 @@ class ChallengeController @Inject() ( Utils.insertIntoJson(jsonBody, "reviewSetting", Challenge.REVIEW_SETTING_NOT_REQUIRED)( IntWrites ) - jsonBody = Utils.insertIntoJson(jsonBody, "layoutJSON", "")(StringWrites) + jsonBody = Utils.insertIntoJson(jsonBody, "widgetLayout", "")(StringWrites) jsonBody = Utils.insertIntoJson(jsonBody, "updateTasks", false)(BooleanWrites) jsonBody = Utils.insertIntoJson(jsonBody, "changesetUrl", false)(BooleanWrites) // if we can't find the parent ID, just use the user's default project instead diff --git a/app/org/maproulette/framework/model/Challenge.scala b/app/org/maproulette/framework/model/Challenge.scala index 34aa391f..3befd51f 100644 --- a/app/org/maproulette/framework/model/Challenge.scala +++ b/app/org/maproulette/framework/model/Challenge.scala @@ -135,7 +135,7 @@ case class ChallengeExtra( taskBundleIdProperty: Option[String] = None, isArchived: Boolean = false, reviewSetting: Int = Challenge.REVIEW_SETTING_NOT_REQUIRED, - layoutJSON: Option[String] = None, + widgetLayout: Option[String] = None, systemArchivedAt: Option[DateTime] = None, presets: Option[List[String]] = None ) extends DefaultWrites diff --git a/app/org/maproulette/framework/repository/ChallengeRepository.scala b/app/org/maproulette/framework/repository/ChallengeRepository.scala index 7d0b940c..542f68f0 100644 --- a/app/org/maproulette/framework/repository/ChallengeRepository.scala +++ b/app/org/maproulette/framework/repository/ChallengeRepository.scala @@ -262,7 +262,7 @@ object ChallengeRepository { get[Option[List[Long]]]("virtual_parent_ids") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[String]]("challenges.layout_json") ~ + get[Option[String]]("challenges.widget_layout") ~ get[Option[DateTime]]("challenges.system_archived_at") map { case id ~ name ~ created ~ modified ~ description ~ infoLink ~ ownerId ~ parentId ~ instruction ~ difficulty ~ blurb ~ enabled ~ featured ~ cooperativeType ~ popularity ~ checkin_comment ~ @@ -270,7 +270,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 ~ layoutJSON ~ systemArchivedAt => + deleted ~ virtualParents ~ isArchived ~ reviewSetting ~ widgetLayout ~ systemArchivedAt => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -326,7 +326,7 @@ object ChallengeRepository { taskBundleIdProperty, isArchived, reviewSetting, - layoutJSON, + widgetLayout, systemArchivedAt ), status, diff --git a/app/org/maproulette/models/dal/ChallengeDAL.scala b/app/org/maproulette/models/dal/ChallengeDAL.scala index b105572c..9c0127db 100644 --- a/app/org/maproulette/models/dal/ChallengeDAL.scala +++ b/app/org/maproulette/models/dal/ChallengeDAL.scala @@ -121,7 +121,7 @@ class ChallengeDAL @Inject() ( get[Boolean]("deleted") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[String]]("challenges.layout_json") ~ + get[Option[String]]("challenges.widget_layout") ~ get[Option[Int]]("challenges.completion_percentage") ~ get[Option[Int]]("challenges.tasks_remaining") map { case id ~ name ~ created ~ modified ~ description ~ infoLink ~ ownerId ~ parentId ~ instruction ~ @@ -131,7 +131,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 ~ layoutJSON ~ completionPercentage ~ tasksRemaining => + requiresLocal ~ deleted ~ isArchived ~ reviewSetting ~ widgetLayout ~ completionPercentage ~ tasksRemaining => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -187,7 +187,7 @@ class ChallengeDAL @Inject() ( taskBundleIdProperty, isArchived, reviewSetting, - layoutJSON + widgetLayout ), status, statusMessage, @@ -257,7 +257,7 @@ class ChallengeDAL @Inject() ( get[Option[List[String]]]("presets") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[String]]("challenges.layout_json") ~ + get[Option[String]]("challenges.widget_layout") ~ get[Option[DateTime]]("challenges.system_archived_at") ~ get[Option[Int]]("challenges.completion_percentage") ~ get[Option[Int]]("challenges.tasks_remaining") map { @@ -269,7 +269,7 @@ class ChallengeDAL @Inject() ( customBasemap ~ updateTasks ~ exportableProperties ~ osmIdProperty ~ taskBundleIdProperty ~ preferredTags ~ preferredReviewTags ~ limitTags ~ limitReviewTags ~ taskStyles ~ lastTaskRefresh ~ dataOriginDate ~ location ~ bounding ~ requiresLocal ~ deleted ~ virtualParents ~ - presets ~ isArchived ~ reviewSetting ~ layoutJSON ~ systemArchivedAt ~ completionPercentage ~ tasksRemaining => + presets ~ isArchived ~ reviewSetting ~ widgetLayout ~ systemArchivedAt ~ completionPercentage ~ tasksRemaining => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -325,7 +325,7 @@ class ChallengeDAL @Inject() ( taskBundleIdProperty, isArchived, reviewSetting, - layoutJSON, + widgetLayout, systemArchivedAt, presets ), @@ -478,7 +478,7 @@ class ChallengeDAL @Inject() ( 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, layout_json) + limit_tags, limit_review_tags, task_styles, requires_local, is_archived, review_setting, 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.general.enabled}, ${challenge.general.featured}, @@ -492,7 +492,7 @@ class ChallengeDAL @Inject() ( ${challenge.dataOriginDate.getOrElse(DateTime.now()).toString}::timestamptz, ${challenge.extra.preferredTags}, ${challenge.extra.preferredReviewTags}, ${challenge.extra.limitTags}, ${challenge.extra.limitReviewTags}, ${challenge.extra.taskStyles}, ${challenge.general.requiresLocal}, ${challenge.extra.isArchived}, - ${challenge.extra.reviewSetting}, ${challenge.extra.layoutJSON}) + ${challenge.extra.reviewSetting}, ${challenge.extra.widgetLayout}) ON CONFLICT(parent_id, LOWER(name)) DO NOTHING RETURNING #${this.retrieveColumns}""" .as(this.parser.*) .headOption @@ -679,9 +679,9 @@ class ChallengeDAL @Inject() ( .asOpt[Int] .getOrElse(cachedItem.extra.reviewSetting) - val layoutJSON = (updates \ "layoutJSON") + val widgetLayout = (updates \ "widgetLayout") .asOpt[String] - .getOrElse(cachedItem.extra.layoutJSON.getOrElse("")) + .getOrElse(cachedItem.extra.widgetLayout.getOrElse("")) val presets: List[String] = (updates \ "presets") .asOpt[List[String]] @@ -712,7 +712,7 @@ class ChallengeDAL @Inject() ( custom_basemap = $customBasemap, updatetasks = $updateTasks, exportable_properties = $exportableProperties, osm_id_property = $osmIdProperty, task_bundle_id_property = $taskBundleIdProperty, preferred_tags = $preferredTags, preferred_review_tags = $preferredReviewTags, limit_tags = $limitTags, limit_review_tags = $limitReviewTags, task_styles = $taskStyles, - requires_local = $requiresLocal, is_archived = $isArchived, review_setting = $reviewSetting, layout_json = $layoutJSON + requires_local = $requiresLocal, is_archived = $isArchived, review_setting = $reviewSetting, widget_layout = $widgetLayout WHERE id = $id RETURNING #${this.retrieveColumns}""".as(parser.*).headOption updatedChallenge match { diff --git a/conf/evolutions/default/93.sql b/conf/evolutions/default/93.sql index 4842113f..12cf3fdb 100644 --- a/conf/evolutions/default/93.sql +++ b/conf/evolutions/default/93.sql @@ -1,7 +1,7 @@ -- --- !Ups ALTER TABLE IF EXISTS challenges -ADD COLUMN layout_json text DEFAULT ''; +ADD COLUMN widget_layout text DEFAULT ''; -- --- !Downs ALTER TABLE IF EXISTS challenges -DROP COLUMN layout_json; \ No newline at end of file +DROP COLUMN widget_layout; \ No newline at end of file From 2ab35a024e5ec10f12cbcca6cb5d261f02208149 Mon Sep 17 00:00:00 2001 From: Lucas Burson Date: Sat, 14 Oct 2023 18:04:47 -0500 Subject: [PATCH 3/6] Refactor to have the widget layout type be a jsonb and JsValue --- .../graphql/schemas/MRSchemaTypes.scala | 8 ++++++- .../framework/model/Challenge.scala | 2 +- .../repository/ChallengeRepository.scala | 6 +++-- .../maproulette/models/dal/ChallengeDAL.scala | 24 ++++++++++++------- conf/evolutions/default/93.sql | 4 ++-- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/org/maproulette/framework/graphql/schemas/MRSchemaTypes.scala b/app/org/maproulette/framework/graphql/schemas/MRSchemaTypes.scala index 5dfecd73..c2e394db 100644 --- a/app/org/maproulette/framework/graphql/schemas/MRSchemaTypes.scala +++ b/app/org/maproulette/framework/graphql/schemas/MRSchemaTypes.scala @@ -170,7 +170,13 @@ trait MRSchemaTypes { implicit val ChallengePriorityType: ObjectType[Unit, ChallengePriority] = deriveObjectType[Unit, ChallengePriority](ObjectTypeName("ChallengePriority")) implicit val ChallengeExtraType: ObjectType[Unit, ChallengeExtra] = - deriveObjectType[Unit, ChallengeExtra](ObjectTypeName("ChallengeExtra")) + deriveObjectType[Unit, ChallengeExtra]( + ObjectTypeName("ChallengeExtra"), + ReplaceField( + "widgetLayout", + Field("widgetLayout", StringType, resolve = _.value.widgetLayout.getOrElse("").toString) + ) + ) // Comment Types implicit val CommentType: ObjectType[Unit, Comment] = deriveObjectType[Unit, Comment](ObjectTypeName("Comment")) diff --git a/app/org/maproulette/framework/model/Challenge.scala b/app/org/maproulette/framework/model/Challenge.scala index 3befd51f..a4cdcfad 100644 --- a/app/org/maproulette/framework/model/Challenge.scala +++ b/app/org/maproulette/framework/model/Challenge.scala @@ -135,7 +135,7 @@ case class ChallengeExtra( taskBundleIdProperty: Option[String] = None, isArchived: Boolean = false, reviewSetting: Int = Challenge.REVIEW_SETTING_NOT_REQUIRED, - widgetLayout: Option[String] = None, + widgetLayout: Option[JsValue] = None, systemArchivedAt: Option[DateTime] = None, presets: Option[List[String]] = None ) extends DefaultWrites diff --git a/app/org/maproulette/framework/repository/ChallengeRepository.scala b/app/org/maproulette/framework/repository/ChallengeRepository.scala index 542f68f0..93c62669 100644 --- a/app/org/maproulette/framework/repository/ChallengeRepository.scala +++ b/app/org/maproulette/framework/repository/ChallengeRepository.scala @@ -8,7 +8,7 @@ package org.maproulette.framework.repository import java.sql.Connection import anorm.SqlParser._ import anorm.{RowParser, SQL, ~} - +import anorm.postgresql.jsValueColumn import javax.inject.{Inject, Singleton} import org.apache.commons.lang3.StringUtils import org.joda.time.DateTime @@ -16,6 +16,7 @@ import org.maproulette.framework.model._ import org.maproulette.framework.psql.{GroupField, Grouping, OR, Query} import org.maproulette.framework.psql.filter._ import play.api.db.Database +import play.api.libs.json.JsValue /** * The challenge repository handles all the querying with the databases related to challenge objects @@ -262,7 +263,7 @@ object ChallengeRepository { get[Option[List[Long]]]("virtual_parent_ids") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[String]]("challenges.widget_layout") ~ + get[Option[JsValue]]("challenges.widget_layout") ~ get[Option[DateTime]]("challenges.system_archived_at") map { case id ~ name ~ created ~ modified ~ description ~ infoLink ~ ownerId ~ parentId ~ instruction ~ difficulty ~ blurb ~ enabled ~ featured ~ cooperativeType ~ popularity ~ checkin_comment ~ @@ -283,6 +284,7 @@ object ChallengeRepository { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r } + new Challenge( id, name, diff --git a/app/org/maproulette/models/dal/ChallengeDAL.scala b/app/org/maproulette/models/dal/ChallengeDAL.scala index 9c0127db..ac246a39 100644 --- a/app/org/maproulette/models/dal/ChallengeDAL.scala +++ b/app/org/maproulette/models/dal/ChallengeDAL.scala @@ -5,9 +5,10 @@ package org.maproulette.models.dal import java.sql.Connection - import anorm.SqlParser._ import anorm._ +import anorm.postgresql.{asJson, jsValueColumn} + import javax.inject.{Inject, Singleton} import org.apache.commons.lang3.StringUtils import org.joda.time.DateTime @@ -17,9 +18,9 @@ import org.maproulette.data.{Actions, ChallengeType, ProjectType} import org.maproulette.exception.{InvalidException, NotFoundException, UniqueViolationException} import org.maproulette.framework.model._ import org.maproulette.framework.repository.{ + ChallengeListingRepository, ProjectRepository, - TaskRepository, - ChallengeListingRepository + TaskRepository } import org.maproulette.framework.service.{ServiceManager, TagService} import org.maproulette.models.dal.mixin.{OwnerMixin, TagDALMixin} @@ -121,7 +122,7 @@ class ChallengeDAL @Inject() ( get[Boolean]("deleted") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[String]]("challenges.widget_layout") ~ + get[Option[JsValue]]("challenges.widget_layout") ~ get[Option[Int]]("challenges.completion_percentage") ~ get[Option[Int]]("challenges.tasks_remaining") map { case id ~ name ~ created ~ modified ~ description ~ infoLink ~ ownerId ~ parentId ~ instruction ~ @@ -144,6 +145,7 @@ class ChallengeDAL @Inject() ( case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r } + new Challenge( id, name, @@ -257,7 +259,7 @@ class ChallengeDAL @Inject() ( get[Option[List[String]]]("presets") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[String]]("challenges.widget_layout") ~ + get[Option[JsValue]]("challenges.widget_layout") ~ get[Option[DateTime]]("challenges.system_archived_at") ~ get[Option[Int]]("challenges.completion_percentage") ~ get[Option[Int]]("challenges.tasks_remaining") map { @@ -282,6 +284,7 @@ class ChallengeDAL @Inject() ( case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r } + new Challenge( id, name, @@ -492,7 +495,8 @@ class ChallengeDAL @Inject() ( ${challenge.dataOriginDate.getOrElse(DateTime.now()).toString}::timestamptz, ${challenge.extra.preferredTags}, ${challenge.extra.preferredReviewTags}, ${challenge.extra.limitTags}, ${challenge.extra.limitReviewTags}, ${challenge.extra.taskStyles}, ${challenge.general.requiresLocal}, ${challenge.extra.isArchived}, - ${challenge.extra.reviewSetting}, ${challenge.extra.widgetLayout}) + ${challenge.extra.reviewSetting}, + ${asJson(challenge.extra.widgetLayout.getOrElse(Json.parse("{}")))} ON CONFLICT(parent_id, LOWER(name)) DO NOTHING RETURNING #${this.retrieveColumns}""" .as(this.parser.*) .headOption @@ -680,8 +684,8 @@ class ChallengeDAL @Inject() ( .getOrElse(cachedItem.extra.reviewSetting) val widgetLayout = (updates \ "widgetLayout") - .asOpt[String] - .getOrElse(cachedItem.extra.widgetLayout.getOrElse("")) + .asOpt[JsValue] + .getOrElse(cachedItem.extra.widgetLayout.getOrElse(Json.parse("{}"))) val presets: List[String] = (updates \ "presets") .asOpt[List[String]] @@ -712,7 +716,9 @@ class ChallengeDAL @Inject() ( custom_basemap = $customBasemap, updatetasks = $updateTasks, exportable_properties = $exportableProperties, osm_id_property = $osmIdProperty, task_bundle_id_property = $taskBundleIdProperty, preferred_tags = $preferredTags, preferred_review_tags = $preferredReviewTags, limit_tags = $limitTags, limit_review_tags = $limitReviewTags, task_styles = $taskStyles, - requires_local = $requiresLocal, is_archived = $isArchived, review_setting = $reviewSetting, widget_layout = $widgetLayout + requires_local = $requiresLocal, is_archived = $isArchived, review_setting = $reviewSetting, widget_layout = ${asJson( + widgetLayout + )} WHERE id = $id RETURNING #${this.retrieveColumns}""".as(parser.*).headOption updatedChallenge match { diff --git a/conf/evolutions/default/93.sql b/conf/evolutions/default/93.sql index 12cf3fdb..c049f9ef 100644 --- a/conf/evolutions/default/93.sql +++ b/conf/evolutions/default/93.sql @@ -1,7 +1,7 @@ -- --- !Ups ALTER TABLE IF EXISTS challenges -ADD COLUMN widget_layout text DEFAULT ''; +ADD COLUMN widget_layout jsonb NOT NULL DEFAULT '{}'::jsonb; -- --- !Downs ALTER TABLE IF EXISTS challenges -DROP COLUMN widget_layout; \ No newline at end of file +DROP COLUMN widget_layout; From 0ac65953d0b652971c1a802192e82c717881bd3e Mon Sep 17 00:00:00 2001 From: Lucas Burson Date: Sat, 14 Oct 2023 19:23:20 -0500 Subject: [PATCH 4/6] Add missing ')' --- app/org/maproulette/models/dal/ChallengeDAL.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/org/maproulette/models/dal/ChallengeDAL.scala b/app/org/maproulette/models/dal/ChallengeDAL.scala index ac246a39..7b517a8e 100644 --- a/app/org/maproulette/models/dal/ChallengeDAL.scala +++ b/app/org/maproulette/models/dal/ChallengeDAL.scala @@ -497,7 +497,7 @@ class ChallengeDAL @Inject() ( ${challenge.extra.limitReviewTags}, ${challenge.extra.taskStyles}, ${challenge.general.requiresLocal}, ${challenge.extra.isArchived}, ${challenge.extra.reviewSetting}, ${asJson(challenge.extra.widgetLayout.getOrElse(Json.parse("{}")))} - ON CONFLICT(parent_id, LOWER(name)) DO NOTHING RETURNING #${this.retrieveColumns}""" + ) ON CONFLICT(parent_id, LOWER(name)) DO NOTHING RETURNING #${this.retrieveColumns}""" .as(this.parser.*) .headOption } From 11597d75b438fa46869f9d9c30bd51ba9e06c617 Mon Sep 17 00:00:00 2001 From: Lucas Burson Date: Sat, 14 Oct 2023 21:39:41 -0500 Subject: [PATCH 5/6] Add a sql comment describing the purpose of challenges.widget_layout --- conf/evolutions/default/93.sql | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/conf/evolutions/default/93.sql b/conf/evolutions/default/93.sql index c049f9ef..2d48f326 100644 --- a/conf/evolutions/default/93.sql +++ b/conf/evolutions/default/93.sql @@ -1,6 +1,9 @@ -- --- !Ups ALTER TABLE IF EXISTS challenges -ADD COLUMN widget_layout jsonb NOT NULL DEFAULT '{}'::jsonb; + ADD COLUMN widget_layout jsonb NOT NULL DEFAULT '{}'::jsonb; + +COMMENT ON COLUMN challenges.widget_layout IS + 'The challenges.widget_layout is a json body that the GUI uses as a "suggested layout" when displaying the challenge to editors.'; -- --- !Downs ALTER TABLE IF EXISTS challenges From 9b465bb1852fa2a9b76160f33a512237e680b0c3 Mon Sep 17 00:00:00 2001 From: Lucas Burson Date: Sun, 15 Oct 2023 18:39:37 -0500 Subject: [PATCH 6/6] Rename widgetLayout to taskWidgetLayout --- .../controllers/api/ChallengeController.scala | 2 +- .../graphql/schemas/MRSchemaTypes.scala | 8 +++++-- .../framework/model/Challenge.scala | 2 +- .../repository/ChallengeRepository.scala | 6 ++--- .../maproulette/models/dal/ChallengeDAL.scala | 24 +++++++++---------- conf/evolutions/default/93.sql | 8 +++---- 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/app/org/maproulette/controllers/api/ChallengeController.scala b/app/org/maproulette/controllers/api/ChallengeController.scala index e9f663e8..6a906c08 100644 --- a/app/org/maproulette/controllers/api/ChallengeController.scala +++ b/app/org/maproulette/controllers/api/ChallengeController.scala @@ -1167,7 +1167,7 @@ class ChallengeController @Inject() ( Utils.insertIntoJson(jsonBody, "reviewSetting", Challenge.REVIEW_SETTING_NOT_REQUIRED)( IntWrites ) - jsonBody = Utils.insertIntoJson(jsonBody, "widgetLayout", "")(StringWrites) + jsonBody = Utils.insertIntoJson(jsonBody, "taskWidgetLayout", "")(StringWrites) jsonBody = Utils.insertIntoJson(jsonBody, "updateTasks", false)(BooleanWrites) jsonBody = Utils.insertIntoJson(jsonBody, "changesetUrl", false)(BooleanWrites) // if we can't find the parent ID, just use the user's default project instead diff --git a/app/org/maproulette/framework/graphql/schemas/MRSchemaTypes.scala b/app/org/maproulette/framework/graphql/schemas/MRSchemaTypes.scala index c2e394db..49b79245 100644 --- a/app/org/maproulette/framework/graphql/schemas/MRSchemaTypes.scala +++ b/app/org/maproulette/framework/graphql/schemas/MRSchemaTypes.scala @@ -173,8 +173,12 @@ trait MRSchemaTypes { deriveObjectType[Unit, ChallengeExtra]( ObjectTypeName("ChallengeExtra"), ReplaceField( - "widgetLayout", - Field("widgetLayout", StringType, resolve = _.value.widgetLayout.getOrElse("").toString) + "taskWidgetLayout", + Field( + "taskWidgetLayout", + StringType, + resolve = _.value.taskWidgetLayout.getOrElse("").toString + ) ) ) // Comment Types diff --git a/app/org/maproulette/framework/model/Challenge.scala b/app/org/maproulette/framework/model/Challenge.scala index a4cdcfad..0b74fe66 100644 --- a/app/org/maproulette/framework/model/Challenge.scala +++ b/app/org/maproulette/framework/model/Challenge.scala @@ -135,7 +135,7 @@ case class ChallengeExtra( taskBundleIdProperty: Option[String] = None, isArchived: Boolean = false, reviewSetting: Int = Challenge.REVIEW_SETTING_NOT_REQUIRED, - widgetLayout: Option[JsValue] = None, + taskWidgetLayout: Option[JsValue] = None, systemArchivedAt: Option[DateTime] = None, presets: Option[List[String]] = None ) extends DefaultWrites diff --git a/app/org/maproulette/framework/repository/ChallengeRepository.scala b/app/org/maproulette/framework/repository/ChallengeRepository.scala index 93c62669..55e5c590 100644 --- a/app/org/maproulette/framework/repository/ChallengeRepository.scala +++ b/app/org/maproulette/framework/repository/ChallengeRepository.scala @@ -263,7 +263,7 @@ object ChallengeRepository { get[Option[List[Long]]]("virtual_parent_ids") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[JsValue]]("challenges.widget_layout") ~ + get[Option[JsValue]]("challenges.task_widget_layout") ~ get[Option[DateTime]]("challenges.system_archived_at") map { case id ~ name ~ created ~ modified ~ description ~ infoLink ~ ownerId ~ parentId ~ instruction ~ difficulty ~ blurb ~ enabled ~ featured ~ cooperativeType ~ popularity ~ checkin_comment ~ @@ -271,7 +271,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 ~ widgetLayout ~ systemArchivedAt => + deleted ~ virtualParents ~ isArchived ~ reviewSetting ~ taskWidgetLayout ~ systemArchivedAt => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -328,7 +328,7 @@ object ChallengeRepository { taskBundleIdProperty, isArchived, reviewSetting, - widgetLayout, + taskWidgetLayout, systemArchivedAt ), status, diff --git a/app/org/maproulette/models/dal/ChallengeDAL.scala b/app/org/maproulette/models/dal/ChallengeDAL.scala index 7b517a8e..82f14db9 100644 --- a/app/org/maproulette/models/dal/ChallengeDAL.scala +++ b/app/org/maproulette/models/dal/ChallengeDAL.scala @@ -122,7 +122,7 @@ class ChallengeDAL @Inject() ( get[Boolean]("deleted") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[JsValue]]("challenges.widget_layout") ~ + get[Option[JsValue]]("challenges.task_widget_layout") ~ get[Option[Int]]("challenges.completion_percentage") ~ get[Option[Int]]("challenges.tasks_remaining") map { case id ~ name ~ created ~ modified ~ description ~ infoLink ~ ownerId ~ parentId ~ instruction ~ @@ -132,7 +132,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 ~ widgetLayout ~ completionPercentage ~ tasksRemaining => + requiresLocal ~ deleted ~ isArchived ~ reviewSetting ~ taskWidgetLayout ~ completionPercentage ~ tasksRemaining => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -189,7 +189,7 @@ class ChallengeDAL @Inject() ( taskBundleIdProperty, isArchived, reviewSetting, - widgetLayout + taskWidgetLayout ), status, statusMessage, @@ -259,7 +259,7 @@ class ChallengeDAL @Inject() ( get[Option[List[String]]]("presets") ~ get[Boolean]("challenges.is_archived") ~ get[Int]("challenges.review_setting") ~ - get[Option[JsValue]]("challenges.widget_layout") ~ + get[Option[JsValue]]("challenges.task_widget_layout") ~ get[Option[DateTime]]("challenges.system_archived_at") ~ get[Option[Int]]("challenges.completion_percentage") ~ get[Option[Int]]("challenges.tasks_remaining") map { @@ -271,7 +271,7 @@ class ChallengeDAL @Inject() ( customBasemap ~ updateTasks ~ exportableProperties ~ osmIdProperty ~ taskBundleIdProperty ~ preferredTags ~ preferredReviewTags ~ limitTags ~ limitReviewTags ~ taskStyles ~ lastTaskRefresh ~ dataOriginDate ~ location ~ bounding ~ requiresLocal ~ deleted ~ virtualParents ~ - presets ~ isArchived ~ reviewSetting ~ widgetLayout ~ systemArchivedAt ~ completionPercentage ~ tasksRemaining => + presets ~ isArchived ~ reviewSetting ~ taskWidgetLayout ~ systemArchivedAt ~ completionPercentage ~ tasksRemaining => val hpr = highPriorityRule match { case Some(c) if StringUtils.isEmpty(c) || StringUtils.equals(c, "{}") => None case r => r @@ -328,7 +328,7 @@ class ChallengeDAL @Inject() ( taskBundleIdProperty, isArchived, reviewSetting, - widgetLayout, + taskWidgetLayout, systemArchivedAt, presets ), @@ -481,7 +481,7 @@ class ChallengeDAL @Inject() ( 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, widget_layout) + 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.general.enabled}, ${challenge.general.featured}, @@ -496,7 +496,7 @@ class ChallengeDAL @Inject() ( ${challenge.extra.preferredTags}, ${challenge.extra.preferredReviewTags}, ${challenge.extra.limitTags}, ${challenge.extra.limitReviewTags}, ${challenge.extra.taskStyles}, ${challenge.general.requiresLocal}, ${challenge.extra.isArchived}, ${challenge.extra.reviewSetting}, - ${asJson(challenge.extra.widgetLayout.getOrElse(Json.parse("{}")))} + ${asJson(challenge.extra.taskWidgetLayout.getOrElse(Json.parse("{}")))} ) ON CONFLICT(parent_id, LOWER(name)) DO NOTHING RETURNING #${this.retrieveColumns}""" .as(this.parser.*) .headOption @@ -683,9 +683,9 @@ class ChallengeDAL @Inject() ( .asOpt[Int] .getOrElse(cachedItem.extra.reviewSetting) - val widgetLayout = (updates \ "widgetLayout") + val taskWidgetLayout = (updates \ "taskWidgetLayout") .asOpt[JsValue] - .getOrElse(cachedItem.extra.widgetLayout.getOrElse(Json.parse("{}"))) + .getOrElse(cachedItem.extra.taskWidgetLayout.getOrElse(Json.parse("{}"))) val presets: List[String] = (updates \ "presets") .asOpt[List[String]] @@ -716,8 +716,8 @@ class ChallengeDAL @Inject() ( custom_basemap = $customBasemap, updatetasks = $updateTasks, exportable_properties = $exportableProperties, osm_id_property = $osmIdProperty, task_bundle_id_property = $taskBundleIdProperty, preferred_tags = $preferredTags, preferred_review_tags = $preferredReviewTags, limit_tags = $limitTags, limit_review_tags = $limitReviewTags, task_styles = $taskStyles, - requires_local = $requiresLocal, is_archived = $isArchived, review_setting = $reviewSetting, widget_layout = ${asJson( - widgetLayout + requires_local = $requiresLocal, is_archived = $isArchived, review_setting = $reviewSetting, task_widget_layout = ${asJson( + taskWidgetLayout )} WHERE id = $id RETURNING #${this.retrieveColumns}""".as(parser.*).headOption diff --git a/conf/evolutions/default/93.sql b/conf/evolutions/default/93.sql index 2d48f326..5bbd3876 100644 --- a/conf/evolutions/default/93.sql +++ b/conf/evolutions/default/93.sql @@ -1,10 +1,10 @@ -- --- !Ups ALTER TABLE IF EXISTS challenges - ADD COLUMN widget_layout jsonb NOT NULL DEFAULT '{}'::jsonb; + ADD COLUMN task_widget_layout jsonb NOT NULL DEFAULT '{}'::jsonb; -COMMENT ON COLUMN challenges.widget_layout IS - 'The challenges.widget_layout is a json body that the GUI uses as a "suggested layout" when displaying the challenge to editors.'; +COMMENT ON COLUMN challenges.task_widget_layout IS + 'The challenges.task_widget_layout is json that the GUI uses as a "suggested layout" when displaying the Task Completion page.'; -- --- !Downs ALTER TABLE IF EXISTS challenges -DROP COLUMN widget_layout; +DROP COLUMN task_widget_layout;