Skip to content

Commit 96e2721

Browse files
authored
Merge pull request #4068 from ProjectSidewalk/develop
v10.4.0
2 parents 030e537 + b8df830 commit 96e2721

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+800
-595
lines changed

app/actor/GetAiValidationsActor.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class GetAiValidationsActor @Inject() (aiService: AiService)(implicit
5454
val currentTimeStart: String = dateFormatter.format(Instant.now())
5555
logger.info(s"Auto-scheduled AI validating started at: $currentTimeStart")
5656
// Try to validate up to 100 labels that haven't yet been validated by AI.
57-
val n: Int = if (config.get[String]("environment-type") == "prod") 500 else 20
57+
val n: Int = if (config.get[String]("environment-type") == "prod") 800 else 10
5858
aiService.validateLabelsWithAiDaily(n).onComplete {
5959
case Success(results) =>
6060
logger.info(s"Attempted ${results.length} AI validations, ${results.flatten.length} successful.")

app/controllers/AdminController.scala

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import formats.json.LabelFormats._
88
import formats.json.UserFormats._
99
import models.auth.{DefaultEnv, WithAdmin}
1010
import models.user.{RoleTable, SidewalkUserWithRole}
11+
import models.validation.LabelValidationTable
1112
import org.apache.pekko.actor.ActorSystem
1213
import org.apache.pekko.dispatch.Dispatcher
1314
import play.api.cache.AsyncCacheApi
@@ -17,7 +18,6 @@ import play.api.{Configuration, Logger}
1718
import play.silhouette.api.Silhouette
1819
import play.silhouette.impl.exceptions.IdentityNotFoundException
1920
import service._
20-
2121
import java.time.format.DateTimeFormatter
2222
import java.time.{Instant, OffsetDateTime, ZoneOffset}
2323
import java.util.concurrent.ThreadPoolExecutor
@@ -120,7 +120,7 @@ class AdminController @Inject() (
120120
def getAllLabels = cc.securityService.SecuredAction(WithAdmin()) { implicit request =>
121121
logger.debug(request.toString) // Added bc scalafmt doesn't like "implicit _" & compiler needs us to use request.
122122
labelService
123-
.selectLocationsAndSeveritiesOfLabels(Seq(), Seq())
123+
.getLabelsForLabelMap(Seq(), Seq(), Seq())
124124
.map { labels =>
125125
val features: Seq[JsObject] = labels.par.map { label =>
126126
Json.obj(
@@ -162,36 +162,39 @@ class AdminController @Inject() (
162162
/**
163163
* Get a list of all labels with metadata needed for /labelMap.
164164
*/
165-
def getAllLabelsForLabelMap(regions: Option[String], routes: Option[String]) = Action.async { implicit request =>
166-
logger.debug(request.toString) // Added bc scalafmt doesn't like "implicit _" & compiler needs us to use request.
167-
val regionIds: Seq[Int] = parseIntegerSeq(regions)
168-
val routeIds: Seq[Int] = parseIntegerSeq(routes)
169-
170-
labelService
171-
.selectLocationsAndSeveritiesOfLabels(regionIds, routeIds)
172-
.map { labels =>
173-
val features: Seq[JsObject] = labels.par.map { label =>
174-
Json.obj(
175-
"type" -> "Feature",
176-
"geometry" -> Json.obj(
177-
"type" -> "Point",
178-
"coordinates" -> Json.arr(label.lng.toDouble, label.lat.toDouble)
179-
),
180-
"properties" -> Json.obj(
181-
"label_id" -> label.labelId,
182-
"label_type" -> label.labelType,
183-
"severity" -> label.severity,
184-
"correct" -> label.correct,
185-
"has_validations" -> label.hasValidations,
186-
"expired" -> label.expired,
187-
"high_quality_user" -> label.highQualityUser
165+
def getAllLabelsForLabelMap(regions: Option[String], routes: Option[String], aiValidationOptions: Option[String]) =
166+
Action.async { implicit request =>
167+
logger.debug(request.toString) // Added bc scalafmt doesn't like "implicit _" & compiler needs us to use request.
168+
val regionIds: Seq[Int] = parseIntegerSeq(regions)
169+
val routeIds: Seq[Int] = parseIntegerSeq(routes)
170+
val aiValOpts: Seq[String] = aiValidationOptions.map(_.split(",").toSeq.distinct).getOrElse(Seq())
171+
172+
labelService
173+
.getLabelsForLabelMap(regionIds, routeIds, aiValOpts)
174+
.map { labels =>
175+
val features: Seq[JsObject] = labels.par.map { label =>
176+
Json.obj(
177+
"type" -> "Feature",
178+
"geometry" -> Json.obj(
179+
"type" -> "Point",
180+
"coordinates" -> Json.arr(label.lng.toDouble, label.lat.toDouble)
181+
),
182+
"properties" -> Json.obj(
183+
"label_id" -> label.labelId,
184+
"label_type" -> label.labelType,
185+
"severity" -> label.severity,
186+
"correct" -> label.correct,
187+
"has_validations" -> label.hasValidations,
188+
"ai_validation" -> label.aiValidation.map(LabelValidationTable.validationOptions.get),
189+
"expired" -> label.expired,
190+
"high_quality_user" -> label.highQualityUser
191+
)
188192
)
189-
)
190-
}.seq
191-
val featureCollection: JsObject = Json.obj("type" -> "FeatureCollection", "features" -> features)
192-
Ok(featureCollection)
193-
}(cpuEc)
194-
}
193+
}.seq
194+
val featureCollection: JsObject = Json.obj("type" -> "FeatureCollection", "features" -> features)
195+
Ok(featureCollection)
196+
}(cpuEc)
197+
}
195198

196199
/**
197200
* Get audit coverage of each neighborhood.

app/controllers/ApplicationController.scala

Lines changed: 8 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import controllers.base._
44
import controllers.helper.ControllerUtils
55
import controllers.helper.ControllerUtils.parseIntegerSeq
66
import models.auth.{DefaultEnv, WithSignedIn}
7-
import models.label.LabelTypeEnum
87
import models.user.SidewalkUserWithRole
98
import models.utils.MyPostgresProfile
109
import play.api.Configuration
@@ -14,7 +13,6 @@ import play.api.mvc._
1413
import play.silhouette.api.Silhouette
1514
import play.silhouette.api.actions.SecuredRequest
1615
import service._
17-
1816
import java.time.OffsetDateTime
1917
import javax.inject._
2018
import scala.concurrent.{ExecutionContext, Future}
@@ -29,8 +27,7 @@ class ApplicationController @Inject() (
2927
userService: UserService,
3028
streetService: StreetService,
3129
labelService: LabelService,
32-
validationService: ValidationService,
33-
regionService: RegionService
30+
validationService: ValidationService
3431
)(implicit ec: ExecutionContext, assets: AssetsFinder)
3532
extends CustomBaseController(cc)
3633
with HasDatabaseConfigProvider[MyPostgresProfile] {
@@ -190,59 +187,17 @@ class ApplicationController @Inject() (
190187
/**
191188
* Returns the LabelMap page that contains a cool visualization.
192189
*/
193-
def labelMap(regions: Option[String], routes: Option[String]) = cc.securityService.SecuredAction { implicit request =>
194-
val regionIds: Seq[Int] = parseIntegerSeq(regions)
195-
val routeIds: Seq[Int] = parseIntegerSeq(routes)
196-
val activityStr: String = if (regions.isEmpty) "Visit_LabelMap" else s"Visit_LabelMap_Regions=$regions"
197-
198-
configService.getCommonPageData(request2Messages.lang).map { commonData =>
199-
cc.loggingService.insert(request.identity.userId, request.ipAddress, activityStr)
200-
Ok(views.html.apps.labelMap(commonData, "Sidewalk - LabelMap", request.identity, regionIds, routeIds))
201-
}
202-
}
203-
204-
/**
205-
* Returns the Gallery page.
206-
*/
207-
def gallery(labelType: String, neighborhoods: String, severities: String, tags: String, validationOptions: String) =
190+
def labelMap(regions: Option[String], routes: Option[String], aiValidationOptions: Option[String]) =
208191
cc.securityService.SecuredAction { implicit request =>
209-
val labelTypes: Seq[(String, String)] = Seq(
210-
("Assorted", Messages("gallery.all")),
211-
(LabelTypeEnum.CurbRamp.name, Messages("curb.ramp")),
212-
(LabelTypeEnum.NoCurbRamp.name, Messages("missing.ramp")),
213-
(LabelTypeEnum.Obstacle.name, Messages("obstacle")),
214-
(LabelTypeEnum.SurfaceProblem.name, Messages("surface.problem")),
215-
(LabelTypeEnum.Occlusion.name, Messages("occlusion")),
216-
(LabelTypeEnum.NoSidewalk.name, Messages("no.sidewalk")),
217-
(LabelTypeEnum.Crosswalk.name, Messages("crosswalk")),
218-
(LabelTypeEnum.Signal.name, Messages("signal")),
219-
(LabelTypeEnum.Other.name, Messages("other"))
220-
)
221-
val labType: String = if (labelTypes.exists(x => { x._1 == labelType })) labelType else "Assorted"
192+
val regionIds: Seq[Int] = parseIntegerSeq(regions)
193+
val routeIds: Seq[Int] = parseIntegerSeq(routes)
194+
val aiValOpts: Seq[String] = aiValidationOptions.map(_.split(",").toSeq.distinct).getOrElse(Seq())
195+
val activityStr: String = if (regions.isEmpty) "Visit_LabelMap" else s"Visit_LabelMap_Regions=$regions"
222196

223-
for {
224-
possibleRegions: Seq[Int] <- regionService.getAllRegions.map(_.map(_.regionId))
225-
possibleTags: Seq[String] <- {
226-
if (labType != "Assorted") db.run(labelService.selectTagsByLabelType(labelType).map(_.map(_.tag)))
227-
else Future.successful(Seq())
228-
}
229-
commonData <- configService.getCommonPageData(request2Messages.lang)
230-
} yield {
231-
// Make sure that list of region IDs, severities, and validation options are formatted correctly.
232-
val regionIdsList: Seq[Int] = parseIntegerSeq(neighborhoods).filter(possibleRegions.contains)
233-
val severityList: Seq[Int] = parseIntegerSeq(severities).filter(s => s > 0 && s < 4)
234-
val tagList: List[String] = tags.split(",").filter(possibleTags.contains).toList
235-
val valOptions: Seq[String] =
236-
validationOptions.split(",").filter(Seq("correct", "incorrect", "unsure", "unvalidated").contains(_)).toSeq
237-
238-
// Log visit to Gallery async.
239-
val activityStr: String =
240-
s"Visit_Gallery_LabelType=${labType}_RegionIDs=${regionIdsList}_Severity=${severityList}_Tags=${tagList}_Validations=$valOptions"
197+
configService.getCommonPageData(request2Messages.lang).map { commonData =>
241198
cc.loggingService.insert(request.identity.userId, request.ipAddress, activityStr)
242-
243199
Ok(
244-
views.html.apps.gallery(commonData, "Sidewalk - Gallery", request.identity, labType, labelTypes,
245-
regionIdsList, severityList, tagList, valOptions)
200+
views.html.apps.labelMap(commonData, "Sidewalk - LabelMap", request.identity, regionIds, routeIds, aiValOpts)
246201
)
247202
}
248203
}

app/controllers/ExploreController.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import play.api.mvc.Result
1515
import play.api.{Configuration, Logger}
1616
import play.silhouette.api.Silhouette
1717
import service.ExploreTaskPostReturnValue
18-
1918
import java.time.OffsetDateTime
2019
import javax.inject.{Inject, Singleton}
2120
import scala.concurrent.{ExecutionContext, Future}
@@ -245,8 +244,8 @@ class ExploreController @Inject() (
245244
aiService
246245
.validateLabelsWithAi(labelsToSend.map(_._1))
247246
.onComplete {
248-
case Success(_) => logger.info("AI validation completed successfully.")
249-
case Failure(e) => logger.error("Error recalculating street priority", e)
247+
case Success(_) => if (labelsToSend.nonEmpty) logger.info(s"AI validation(s) completed successfully.")
248+
case Failure(e) => logger.error("Error occurred when submitting AI validations:", e)
250249
}
251250

252251
// Send contributions to SciStarter async so that it can be recorded in their user dashboard there.

app/controllers/GalleryController.scala

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,95 @@
11
package controllers
22

33
import controllers.base._
4+
import controllers.helper.ControllerUtils.parseIntegerSeq
45
import formats.json.GalleryFormats._
56
import formats.json.LabelFormats
67
import models.auth.DefaultEnv
78
import models.gallery.{GalleryTaskEnvironment, GalleryTaskEnvironmentTable, GalleryTaskInteraction, GalleryTaskInteractionTable}
9+
import models.label.LabelTypeEnum
810
import models.utils.MyPostgresProfile
11+
import play.api.Configuration
912
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
13+
import play.api.i18n.Messages
1014
import play.api.libs.json.{JsError, JsObject, JsValue, Json}
11-
import play.api.mvc.{Action, Result}
15+
import play.api.mvc.{Action, AnyContent, Result}
1216
import play.silhouette.api.Silhouette
13-
import service.{GsvDataService, LabelService}
14-
17+
import service.{ConfigService, GsvDataService, LabelService, RegionService}
1518
import javax.inject.{Inject, Singleton}
1619
import scala.concurrent.{ExecutionContext, Future}
1720

1821
@Singleton
1922
class GalleryController @Inject() (
2023
cc: CustomControllerComponents,
2124
val silhouette: Silhouette[DefaultEnv],
25+
val config: Configuration,
2226
protected val dbConfigProvider: DatabaseConfigProvider,
2327
implicit val ec: ExecutionContext,
28+
configService: ConfigService,
2429
labelService: LabelService,
2530
gsvDataService: GsvDataService,
2631
galleryTaskInteractionTable: GalleryTaskInteractionTable,
27-
galleryTaskEnvironmentTable: GalleryTaskEnvironmentTable
28-
) extends CustomBaseController(cc)
32+
galleryTaskEnvironmentTable: GalleryTaskEnvironmentTable,
33+
regionService: RegionService
34+
)(implicit assets: AssetsFinder)
35+
extends CustomBaseController(cc)
2936
with HasDatabaseConfigProvider[MyPostgresProfile] {
37+
implicit val implicitConfig: Configuration = config
38+
39+
/**
40+
* Returns the Gallery page.
41+
*/
42+
def gallery(
43+
labelType: String,
44+
neighborhoods: String,
45+
severities: String,
46+
tags: String,
47+
validationOptions: String,
48+
aiValidationOptions: String
49+
): Action[AnyContent] =
50+
cc.securityService.SecuredAction { implicit request =>
51+
val labelTypes: Seq[(String, String)] = Seq(
52+
("Assorted", Messages("gallery.all")),
53+
(LabelTypeEnum.CurbRamp.name, Messages("curb.ramp")),
54+
(LabelTypeEnum.NoCurbRamp.name, Messages("missing.ramp")),
55+
(LabelTypeEnum.Obstacle.name, Messages("obstacle")),
56+
(LabelTypeEnum.SurfaceProblem.name, Messages("surface.problem")),
57+
(LabelTypeEnum.Occlusion.name, Messages("occlusion")),
58+
(LabelTypeEnum.NoSidewalk.name, Messages("no.sidewalk")),
59+
(LabelTypeEnum.Crosswalk.name, Messages("crosswalk")),
60+
(LabelTypeEnum.Signal.name, Messages("signal")),
61+
(LabelTypeEnum.Other.name, Messages("other"))
62+
)
63+
val labType: String = if (labelTypes.exists(x => { x._1 == labelType })) labelType else "Assorted"
64+
65+
for {
66+
possibleRegions: Seq[Int] <- regionService.getAllRegions.map(_.map(_.regionId))
67+
possibleTags: Seq[String] <- {
68+
if (labType != "Assorted") db.run(labelService.selectTagsByLabelType(labelType).map(_.map(_.tag)))
69+
else Future.successful(Seq())
70+
}
71+
commonData <- configService.getCommonPageData(request2Messages.lang)
72+
} yield {
73+
// Make sure that list of region IDs, severities, and validation options are formatted correctly.
74+
val regionIdsList: Seq[Int] = parseIntegerSeq(neighborhoods).filter(possibleRegions.contains)
75+
val severityList: Seq[Int] = parseIntegerSeq(severities).filter(s => s > 0 && s < 4)
76+
val tagList: List[String] = tags.split(",").filter(possibleTags.contains).toList
77+
val valOptions: Seq[String] =
78+
validationOptions.split(",").filter(Seq("correct", "incorrect", "unsure", "unvalidated").contains(_)).toSeq
79+
val aiValOptions: Seq[String] =
80+
aiValidationOptions.split(",").filter(Seq("correct", "incorrect", "unsure", "unvalidated").contains(_)).toSeq
81+
82+
// Log visit to Gallery async.
83+
val activityStr: String =
84+
s"Visit_Gallery_LabelType=${labType}_RegionIDs=${regionIdsList}_Severity=${severityList}_Tags=${tagList}_Validations=$valOptions"
85+
cc.loggingService.insert(request.identity.userId, request.ipAddress, activityStr)
86+
87+
Ok(
88+
views.html.apps.gallery(commonData, "Sidewalk - Gallery", request.identity, labType, labelTypes,
89+
regionIdsList, severityList, tagList, valOptions, aiValOptions)
90+
)
91+
}
92+
}
3093

3194
/**
3295
* Returns labels of specified type, severities, and tags.
@@ -36,18 +99,19 @@ class GalleryController @Inject() (
3699
submission.fold(
37100
errors => { Future.successful(BadRequest(Json.obj("status" -> "Error", "message" -> JsError.toJson(errors)))) },
38101
submission => {
39-
val n: Int = submission.n
40-
val labelTypeId: Option[Int] = submission.labelTypeId
41-
val loadedLabelIds: Set[Int] = submission.loadedLabels.toSet
42-
val valOptions: Set[String] = submission.validationOptions.getOrElse(Seq()).toSet
43-
val regionIds: Set[Int] = submission.regionIds.getOrElse(Seq()).toSet
44-
val severities: Set[Int] = submission.severities.getOrElse(Seq()).toSet
45-
val tags: Set[String] = submission.tags.getOrElse(Seq()).toSet
46-
val userId: String = request.identity.userId
102+
val n: Int = submission.n
103+
val labelType: Option[LabelTypeEnum.Base] = submission.labelTypeId.flatMap(l => LabelTypeEnum.byId.get(l))
104+
val loadedLabels: Set[Int] = submission.loadedLabels.toSet
105+
val valOptions: Set[String] = submission.validationOptions.getOrElse(Seq()).toSet
106+
val regionIds: Set[Int] = submission.regionIds.getOrElse(Seq()).toSet
107+
val severities: Set[Int] = submission.severities.getOrElse(Seq()).toSet
108+
val tags: Set[String] = submission.tags.getOrElse(Seq()).toSet
109+
val aiValOptions: Set[String] = submission.aiValidationOptions.getOrElse(Seq()).toSet
110+
val userId: String = request.identity.userId
47111

48112
// Get labels from LabelTable.
49113
labelService
50-
.getGalleryLabels(n, labelTypeId, loadedLabelIds, valOptions, regionIds, severities, tags, userId)
114+
.getGalleryLabels(n, labelType, loadedLabels, valOptions, regionIds, severities, tags, aiValOptions, userId)
51115
.map { labels =>
52116
val jsonList: Seq[JsObject] = labels.map(l =>
53117
Json.obj(

app/controllers/UserProfileController.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import play.api.libs.json.{JsObject, Json}
1515
import play.api.{Configuration, Logger}
1616
import play.silhouette.api.Silhouette
1717
import play.silhouette.impl.exceptions.IdentityNotFoundException
18-
1918
import javax.inject._
2019
import scala.concurrent.{ExecutionContext, Future}
2120

@@ -146,10 +145,10 @@ class UserProfileController @Inject() (
146145
logger.debug(request.toString) // Added bc scalafmt doesn't like "implicit _" & compiler needs us to use request.
147146
authenticationService.findByUserId(userId).flatMap {
148147
case Some(user) =>
149-
val labelTypes: Set[String] = LabelTypeEnum.primaryValidateLabelTypes
148+
val labelTypes: Set[LabelTypeEnum.Base] = LabelTypeEnum.primaryValidateLabelTypes
150149
labelService.getRecentValidatedLabelsForUser(userId, labelTypes, n).map { validations =>
151150
val validationJson = Json.toJson(labelTypes.map { labelType =>
152-
labelType -> validations(labelType).map { l =>
151+
labelType.name -> validations(labelType).map { l =>
153152
val imageUrl: String =
154153
gsvDataService.getImageUrl(l.gsvPanoramaId, l.pov.heading, l.pov.pitch, l.pov.zoom)
155154
labelMetadataUserDashToJson(l, imageUrl)

app/controllers/ValidateController.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import play.api.Configuration
1616
import play.api.libs.json._
1717
import play.api.mvc.Result
1818
import service.ValidationSubmission
19-
2019
import java.time.OffsetDateTime
2120
import java.time.temporal.ChronoUnit
2221
import java.util.UUID
@@ -245,7 +244,7 @@ class ValidateController @Inject() (
245244
if (parsedLabelTypeId.isDefined && parsedLabelTypeId.get.isEmpty) {
246245
(
247246
ValidateParams(adminVersion),
248-
BadRequest(s"Invalid label type provided: ${labelType.get}. Valid label types are: ${LabelTypeEnum.primaryLabelTypes.mkString(", ")}. Or you can use their IDs: ${LabelTypeEnum.primaryLabelTypeIds.mkString(", ")}.")
247+
BadRequest(s"Invalid label type provided: ${labelType.get}. Valid label types are: ${LabelTypeEnum.primaryLabelTypeNames.mkString(", ")}. Or you can use their IDs: ${LabelTypeEnum.primaryLabelTypeIds.mkString(", ")}.")
249248
)
250249
} else if (userIds.isDefined && userIds.get.length != userIds.get.flatten.length) {
251250
(

0 commit comments

Comments
 (0)