From f40b1541e4e9dbcccaf11566f69877d5fe24b430 Mon Sep 17 00:00:00 2001 From: leejoh22 Date: Thu, 24 Oct 2024 16:59:05 -0700 Subject: [PATCH 01/36] Added label validation filter and popup to user profile map --- Gruntfile.js | 4 + app/controllers/UserProfileController.scala | 4 +- app/models/label/LabelTable.scala | 8 +- app/views/labelMap.scala.html | 2 +- app/views/userProfile.scala.html | 22 +++++- .../Admin/src/Admin.GSVLabelView.js | 76 +++++++++++-------- public/javascripts/Admin/src/Admin.js | 2 +- public/javascripts/PSMap/AddLabelsToMap.js | 14 +++- public/javascripts/PSMap/PSMapUtilities.js | 16 +++- public/javascripts/Progress/src/Progress.js | 20 ++++- public/stylesheets/userProfile.css | 1 + 11 files changed, 122 insertions(+), 47 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 9ee42790de..8e685585e6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -39,6 +39,10 @@ module.exports = function(grunt) { }, dist_progress: { src: [ + 'public/javascripts/Admin/src/*.js', + 'public/javascripts/SVValidate/src/util/*.js', + 'public/javascripts/common/Panomarker.js', + 'public/javascripts/common/UtilitiesPanomarker.js', 'public/javascripts/Progress/src/*.js', 'public/javascripts/common/Utilities.js', 'public/javascripts/common/UtilitiesSidewalk.js', diff --git a/app/controllers/UserProfileController.scala b/app/controllers/UserProfileController.scala index c37b5961f8..cd53b22f23 100644 --- a/app/controllers/UserProfileController.scala +++ b/app/controllers/UserProfileController.scala @@ -111,7 +111,9 @@ class UserProfileController @Inject() (implicit val env: Environment[User, Sessi "audit_task_id" -> label.auditTaskId, "label_id" -> label.labelId, "gsv_panorama_id" -> label.gsvPanoramaId, - "label_type" -> label.labelType + "label_type" -> label.labelType, + "correct" -> label.correct, + "has_validations" -> label.hasValidations ) Json.obj("type" -> "Feature", "geometry" -> point, "properties" -> properties) } diff --git a/app/models/label/LabelTable.scala b/app/models/label/LabelTable.scala index 1b4835bafb..244823f4be 100644 --- a/app/models/label/LabelTable.scala +++ b/app/models/label/LabelTable.scala @@ -40,7 +40,7 @@ case class POV(heading: Double, pitch: Double, zoom: Int) case class Dimensions(width: Int, height: Int) case class LocationXY(x: Int, y: Int) -case class LabelLocation(labelId: Int, auditTaskId: Int, gsvPanoramaId: String, labelType: String, lat: Float, lng: Float) +case class LabelLocation(labelId: Int, auditTaskId: Int, gsvPanoramaId: String, labelType: String, lat: Float, lng: Float, correct: Option[Boolean], hasValidations: Boolean) case class LabelLocationWithSeverity(labelId: Int, auditTaskId: Int, labelType: String, lat: Float, lng: Float, correct: Option[Boolean], hasValidations: Boolean, expired: Boolean, @@ -250,7 +250,7 @@ object LabelTable { )) implicit val labelLocationConverter = GetResult[LabelLocation](r => - LabelLocation(r.nextInt, r.nextInt, r.nextString, r.nextString, r.nextFloat, r.nextFloat)) + LabelLocation(r.nextInt, r.nextInt, r.nextString, r.nextString, r.nextFloat, r.nextFloat, r.nextBooleanOption, r.nextBoolean)) implicit val labelSeverityConverter = GetResult[LabelLocationWithSeverity](r => LabelLocationWithSeverity(r.nextInt, r.nextInt, r.nextString, r.nextFloat, r.nextFloat, r.nextBooleanOption, @@ -1038,8 +1038,8 @@ object LabelTable { if _l.userId === userId.toString if regionId.isEmpty.asColumnOf[Boolean] || _ser.regionId === regionId.getOrElse(-1) if _lp.lat.isDefined && _lp.lng.isDefined - } yield (_l.labelId, _l.auditTaskId, _l.gsvPanoramaId, _lt.labelType, _lp.lat.get, _lp.lng.get) - _labels.list.map(LabelLocation.tupled) + } yield (_l.labelId, _l.auditTaskId, _l.gsvPanoramaId, _lt.labelType, _lp.lat, _lp.lng, _l.correct, _l.agreeCount > 0 || _l.disagreeCount > 0 || _l.unsureCount > 0) + _labels.list.map(l => LabelLocation(l._1, l._2, l._3, l._4, l._5.get, l._6.get, l._7, l._8)) } /** diff --git a/app/views/labelMap.scala.html b/app/views/labelMap.scala.html index 79b3cc0164..e17942977c 100644 --- a/app/views/labelMap.scala.html +++ b/app/views/labelMap.scala.html @@ -226,7 +226,7 @@ } toggleLabelLayer(checkbox.id.split('-')[0], checkbox, slider, map, mapData); } else if (checkbox.getAttribute('data-filter-type') === 'label-validations') { - filterLabelLayers(checkbox, map, mapData); + filterLabelLayers(checkbox, map, mapData, true); } else if (checkbox.getAttribute('data-filter-type') === 'misc') { toggleMiscLayers(checkbox, map, mapData); } else { diff --git a/app/views/userProfile.scala.html b/app/views/userProfile.scala.html index 01a4513613..ceab169457 100644 --- a/app/views/userProfile.scala.html +++ b/app/views/userProfile.scala.html @@ -79,6 +79,26 @@ @Messages("audited.street") + + + @Messages("validated.correct") + + + + + @Messages("validated.incorrect") + + + + + @Messages("unsure.caps") + + + + + @Messages("unvalidated") + + @@ -208,7 +228,7 @@

@Messages("dashboard.mistakes.header")

i18next.use(i18nextHttpBackend).init({ backend: { loadPath: '/assets/locales/{{lng}}/{{ns}}.json' }, fallbackLng: 'en', - ns: ['dashboard', 'common'], + ns: ['dashboard', 'common', 'labelmap'], defaultNS: 'dashboard', lng: "@lang.code", debug: false diff --git a/public/javascripts/Admin/src/Admin.GSVLabelView.js b/public/javascripts/Admin/src/Admin.GSVLabelView.js index ef95cf38e2..e266f314ef 100644 --- a/public/javascripts/Admin/src/Admin.GSVLabelView.js +++ b/public/javascripts/Admin/src/Admin.GSVLabelView.js @@ -188,24 +188,45 @@ function AdminGSVLabelView(admin, source) { } self.prevAction = null; - self.agreeButton.click(function() { - if (self.prevAction !== "Agree") { - _disableValidationButtons(); - _validateLabel("Agree"); - } - }); - self.disagreeButton.click(function() { - if (self.prevAction !== "Disagree") { - _disableValidationButtons(); - _validateLabel("Disagree"); - } - }); - self.unsureButton.click(function() { - if (self.prevAction !== "Unsure") { - _disableValidationButtons(); - _validateLabel("Unsure"); - } - }); + self.commentButton = self.modal.find("#comment-button"); + self.commentTextArea = self.modal.find("#comment-textarea"); + + // Hide validation buttons and comment submission for images labeled by current user + if (self.source === "UserMap") { + self.modal.find('#validation-input-holder h3').hide(); + _hideValidationButtons(); + self.commentButton.hide(); + self.commentTextArea.hide(); + } else { + self.agreeButton.click(function () { + if (self.prevAction !== "Agree") { + _disableValidationButtons(); + _validateLabel("Agree"); + } + }); + self.disagreeButton.click(function () { + if (self.prevAction !== "Disagree") { + _disableValidationButtons(); + _validateLabel("Disagree"); + } + }); + self.unsureButton.click(function () { + if (self.prevAction !== "Unsure") { + _disableValidationButtons(); + _validateLabel("Unsure"); + } + }); + + self.commentButton.popover({ + template : '' + }); + self.commentButton.click(function() { + var comment = self.commentTextArea.val(); + if (comment) { + _submitComment(comment); + } + }); + } self.lowQualityButton.click(function() { _setFlag("low_quality", !self.flags["low_quality"]); @@ -217,18 +238,6 @@ function AdminGSVLabelView(admin, source) { _setFlag("stale", !self.flags["stale"]); }); - self.commentButton = self.modal.find("#comment-button"); - self.commentTextArea = self.modal.find("#comment-textarea"); - self.commentButton.popover({ - template : '' - }); - self.commentButton.click(function() { - var comment = self.commentTextArea.val(); - if (comment) { - _submitComment(comment); - } - }); - self.modalTitle = self.modal.find("#myModalLabel"); self.modalTimestamp = self.modal.find("#timestamp"); self.modalLabelTypeValue = self.modal.find("#label-type-value"); @@ -343,6 +352,13 @@ function AdminGSVLabelView(admin, source) { } } } + function _hideValidationButtons() { + for (var button in self.resultButtons) { + if (self.resultButtons.hasOwnProperty(button)) { + self.resultButtons[button].hide(); + } + } + } /** * Creates the validation row text and displays it in the label. diff --git a/public/javascripts/Admin/src/Admin.js b/public/javascripts/Admin/src/Admin.js index 25349e31a1..4bcba98244 100644 --- a/public/javascripts/Admin/src/Admin.js +++ b/public/javascripts/Admin/src/Admin.js @@ -200,7 +200,7 @@ function Admin(_, $) { } toggleLabelLayer(checkbox.id.split('-')[0], checkbox, slider, map, mapData); } else if (checkbox.getAttribute('data-filter-type') === 'label-validations') { - filterLabelLayers(checkbox, map, mapData); + filterLabelLayers(checkbox, map, mapData, true); } else { filterStreetLayer(map); } diff --git a/public/javascripts/PSMap/AddLabelsToMap.js b/public/javascripts/PSMap/AddLabelsToMap.js index 347bba3840..6671039ad6 100644 --- a/public/javascripts/PSMap/AddLabelsToMap.js +++ b/public/javascripts/PSMap/AddLabelsToMap.js @@ -53,9 +53,19 @@ function AddLabelsToMap(map, labelData, params) { mapData.sortedLabels['Crosswalk'].map(l => l.length).reduce((acc, len) => acc + len, 0); document.getElementById('td-number-of-signals').innerHTML = mapData.sortedLabels['Signal'].map(l => l.length).reduce((acc, len) => acc + len, 0); + + filterLabelLayers('incorrect', map, mapData, false); } else { - // For LabelMap. Set up the initial set of filters. - filterLabelLayers('incorrect', map, mapData); + // For LabelMap. + document.getElementById('map-legend-other').innerHTML = + ""; + document.getElementById('map-legend-occlusion').innerHTML = + ""; + + // Set up the initial set of filters. + filterLabelLayers('incorrect', map, mapData, true); } // Set up the label hover and popup functionality. diff --git a/public/javascripts/PSMap/PSMapUtilities.js b/public/javascripts/PSMap/PSMapUtilities.js index 03ba40c494..93e5c121c0 100644 --- a/public/javascripts/PSMap/PSMapUtilities.js +++ b/public/javascripts/PSMap/PSMapUtilities.js @@ -39,13 +39,13 @@ function toggleLabelLayer(labelType, checkbox, slider, map, mapData) { * @param map The Mapbox map object. * @param mapData */ -function filterLabelLayers(checkbox, map, mapData) { +function filterLabelLayers(checkbox, map, mapData, labelMap) { if (checkbox) mapData[checkbox.id] = checkbox.checked; Object.keys(mapData.layerNames).forEach(function (key) { for (let i = 0; i < mapData.layerNames[key].length; i++) { - map.setFilter(mapData.layerNames[key][i], [ + // Create the base filter + let filter = [ 'all', - ['any', mapData.lowQualityUsers, ['==', ['get', 'high_quality_user'], true]], [ 'any', ['all', mapData.correct, ['==', ['get', 'correct'], true]], @@ -53,7 +53,15 @@ function filterLabelLayers(checkbox, map, mapData) { ['all', mapData.unsure, ['==', ['get', 'correct'], null], ['==', ['get', 'has_validations'], true]], ['all', mapData.unvalidated, ['==', ['get', 'correct'], null], ['==', ['get', 'has_validations'], false]] ] - ]); + ]; + + // Conditionally add the high_quality_user filter if labelMap is true + if (labelMap) { + filter.push(['any', mapData.lowQualityUsers, ['==', ['get', 'high_quality_user'], true]]); + } + + // Apply the filter to the map layer + map.setFilter(mapData.layerNames[key][i], filter); } }); } diff --git a/public/javascripts/Progress/src/Progress.js b/public/javascripts/Progress/src/Progress.js index 01391af11d..8f304ebc41 100644 --- a/public/javascripts/Progress/src/Progress.js +++ b/public/javascripts/Progress/src/Progress.js @@ -12,13 +12,17 @@ function Progress (_, $, userRole) { neighborhoodTooltip: 'completionRate', neighborhoodFillColor: '#5d6d6b', neighborhoodFillOpacity: 0.1, + popupLabelViewer: AdminGSVLabelView(false, "UserMap"), includeLabelCounts: true }; + var self = {} CreatePSMap($, params).then(m => { - window.map = m[0]; - setRegionFocus(window.map); + self.map = m[0]; + self.mapData = m[3]; + setRegionFocus(self.map); + addLegendListeners(self.map, self.mapData); }); - + window.map = self; // Get total reward if a turker. if (userRole === 'Turker') { $.ajax({ @@ -76,5 +80,15 @@ function Progress (_, $, userRole) { }); } + function addLegendListeners(map, mapData) { + // Add listeners on the checkboxes. + $('#map-label-legend tr input[type="checkbox"]').each(function () { + $(this).on('click', () => { + filterLabelLayers(this, map, mapData, false); + }); + this.disabled = false; // Enable the checkbox now that the map has loaded. + }); + } + $('.put-user-org').on('click', putUserOrg); } diff --git a/public/stylesheets/userProfile.css b/public/stylesheets/userProfile.css index b2dea122fe..b7a8803d18 100644 --- a/public/stylesheets/userProfile.css +++ b/public/stylesheets/userProfile.css @@ -18,6 +18,7 @@ svg { #map-label-legend table td { border-top: 0; + padding: 2px; } #profile-container { From e7d37ed4af16a707e026ec406bfc392488930fa1 Mon Sep 17 00:00:00 2001 From: aslassi777 Date: Sun, 27 Oct 2024 22:40:15 -0700 Subject: [PATCH 02/36] Finally fixed dev environment. Reset original push to fix change #3313, enabling ability and functionality to create teams, and changing UI for the dashboard in regards to that portion of website. --- app/controllers/AdminController.scala | 2 +- app/controllers/UserProfileController.scala | 21 +++++++- app/models/user/OrganizationTable.scala | 14 +++++ app/models/user/UserOrgTable.scala | 8 +-- app/views/leaderboard.scala.html | 10 ++-- app/views/userProfile.scala.html | 60 ++++++++++++++++----- conf/messages.de | 16 +++--- conf/messages.en | 16 +++--- conf/messages.es | 16 +++--- conf/messages.nl | 16 +++--- conf/messages.zh-TW | 18 ++++--- conf/routes | 2 + public/javascripts/Progress/src/Progress.js | 52 ++++++++++++++---- public/stylesheets/userProfile.css | 4 ++ 14 files changed, 190 insertions(+), 65 deletions(-) diff --git a/app/controllers/AdminController.scala b/app/controllers/AdminController.scala index 44305ae84d..af38ebc517 100644 --- a/app/controllers/AdminController.scala +++ b/app/controllers/AdminController.scala @@ -571,7 +571,7 @@ class AdminController @Inject() (implicit val env: Environment[User, SessionAuth if (isAdmin(request.identity)) { // Remove any previous org and add the new org. Will add the ability to be in multiple orgs in the future. - val currentOrg: Option[Int] = UserOrgTable.getAllOrgs(userId).headOption + val currentOrg: Option[Int] = UserOrgTable.getOrg(userId).headOption if (currentOrg.nonEmpty) { UserOrgTable.remove(userId, currentOrg.get) } diff --git a/app/controllers/UserProfileController.scala b/app/controllers/UserProfileController.scala index c37b5961f8..5d6f2d61f2 100644 --- a/app/controllers/UserProfileController.scala +++ b/app/controllers/UserProfileController.scala @@ -19,6 +19,8 @@ import play.api.libs.json.{JsObject, JsValue, Json} import play.extras.geojson import play.api.i18n.Messages import scala.concurrent.Future +import play.api.mvc._ +import models.user.OrganizationTable /** * Holds the HTTP requests associated with the user dashboard. @@ -183,7 +185,7 @@ class UserProfileController @Inject() (implicit val env: Environment[User, Sessi case Some(user) => val userId: UUID = user.userId if (user.role.getOrElse("") != "Anonymous") { - val allUserOrgs: List[Int] = UserOrgTable.getAllOrgs(userId) + val allUserOrgs: Option[Int] = UserOrgTable.getOrg(userId) if (allUserOrgs.headOption.isEmpty) { UserOrgTable.save(userId, orgId) } else if (allUserOrgs.head != orgId) { @@ -197,6 +199,23 @@ class UserProfileController @Inject() (implicit val env: Environment[User, Sessi } } + /** + * Creates a team and puts them in the organization table. + */ + def createTeam() = Action(parse.json) { request => + val orgName = (request.body \ "name").as[String] + val orgDescription = (request.body \ "description").as[String] + + // Inserting into the database and capturing the generated orgId + val orgId = OrganizationTable.insert(orgName, orgDescription) + + Ok(Json.obj( + "message" -> "Organization created successfully!", + "org_id" -> orgId + )) +} + + /** * Gets some basic stats about the logged in user that we show across the site: distance, label count, and accuracy. */ diff --git a/app/models/user/OrganizationTable.scala b/app/models/user/OrganizationTable.scala index 8bf9123cb8..3205aa4121 100644 --- a/app/models/user/OrganizationTable.scala +++ b/app/models/user/OrganizationTable.scala @@ -58,4 +58,18 @@ object OrganizationTable { def getOrganizationDescription(orgId: Int): Option[String] = db.withTransaction { implicit session => organizations.filter(_.orgId === orgId).map(_.orgDescription).firstOption } + + /** + * Inserts a new organization into the database. + * + * @param orgName The name of the organization to be created. + * @param orgDescription A brief description of the organization. + * @return The auto-generated ID of the newly created organization. + */ + def insert(orgName: String, orgDescription: String): Int = { + val newOrganization = Organization(0, orgName, orgDescription) // orgId is auto-generated + db.withTransaction { implicit session => + (organizations returning organizations.map(_.orgId)) += newOrganization + } + } } diff --git a/app/models/user/UserOrgTable.scala b/app/models/user/UserOrgTable.scala index fafc5b584e..147dcdae0f 100644 --- a/app/models/user/UserOrgTable.scala +++ b/app/models/user/UserOrgTable.scala @@ -22,13 +22,13 @@ object UserOrgTable { val userOrgs = TableQuery[UserOrgTable] /** - * Gets all organizations the given user is affiliated with. + * Gets the organization the given user is affiliated with. * * @param userId The id of the user. - * @return A list of all organizations the given user is affiliated with. + * @return The organization the given user is affiliated with. */ - def getAllOrgs(userId: UUID): List[Int] = db.withSession { implicit session => - userOrgs.filter(_.userId === userId.toString).map(_.orgId).list + def getOrg(userId: UUID): Option[Int] = db.withSession { implicit session => + userOrgs.filter(_.userId === userId.toString).map(_.orgId).firstOption } /** diff --git a/app/views/leaderboard.scala.html b/app/views/leaderboard.scala.html index ec97e1ffd3..69d8814253 100644 --- a/app/views/leaderboard.scala.html +++ b/app/views/leaderboard.scala.html @@ -8,7 +8,7 @@ @leaderboardStats = @{UserStatTable.getLeaderboardStats(10)} @leaderboardStatsThisWeek = @{UserStatTable.getLeaderboardStats(10, "weekly")} @leaderboardStatsByOrg = @{UserStatTable.getLeaderboardStats(10, "overall", true)} -@leaderboardStatsOrg = @{UserStatTable.getLeaderboardStats(10, "overall", false, UserOrgTable.getAllOrgs(user.get.userId).headOption)} +@leaderboardStatsOrg = @{UserStatTable.getLeaderboardStats(10, "overall", false, UserOrgTable.getOrg(user.get.userId).headOption)} @currentCountryId =@{Configs.getCurrentCountryId()}
@@ -239,10 +239,10 @@

@Messages("leaderboard.weekly.title

} - @if(user && user.get.role.getOrElse("") != "Anonymous" && !UserOrgTable.getAllOrgs(user.get.userId).isEmpty) { + @if(user && user.get.role.getOrElse("") != "Anonymous" && !UserOrgTable.getOrg(user.get.userId).isEmpty) {
-

@Messages("leaderboard.org.title", OrganizationTable.getOrganizationName(UserOrgTable.getAllOrgs(user.get.userId).head).getOrElse(""))

-
@Messages("leaderboard.org.detail", OrganizationTable.getOrganizationName(UserOrgTable.getAllOrgs(user.get.userId).head).getOrElse(""))
+

@Messages("leaderboard.org.title", OrganizationTable.getOrganizationName(UserOrgTable.getOrg(user.get.userId).head).getOrElse(""))

+
@Messages("leaderboard.org.detail", OrganizationTable.getOrganizationName(UserOrgTable.getOrg(user.get.userId).head).getOrElse(""))
@@ -325,7 +325,7 @@

@if(user && user.get.role.getOrElse("") == "Anonymous") { @Html(Messages("leaderboard.encouragement.no.user")) } else { - @if(UserOrgTable.getAllOrgs(user.get.userId).isEmpty) { + @if(UserOrgTable.getOrg(user.get.userId).isEmpty) { @if(currentCountryId == "taiwan") { @Html(Messages("leaderboard.encouragement.no.org", routes.Assets.at("documents/labeling-guide-Taiwan.pdf").url)) } else { diff --git a/app/views/userProfile.scala.html b/app/views/userProfile.scala.html index 01a4513613..2f1c8439ad 100644 --- a/app/views/userProfile.scala.html +++ b/app/views/userProfile.scala.html @@ -5,6 +5,7 @@ @import models.label.LabelTable @import models.label.LabelValidationTable @(title: String, user: Option[User] = None, auditedDistance: Float)(implicit lang: Lang) +@userOrg = @{UserOrgTable.getOrg(user.get.userId)} @main(title) { @navbar(user, user.map(u=> "/dashboard")) @@ -14,25 +15,28 @@
@user.get.username
- @if(!UserOrgTable.getAllOrgs(user.get.userId).isEmpty) { -
@OrganizationTable.getOrganizationName(UserOrgTable.getAllOrgs(user.get.userId).head)
- } else { -
@Messages("dashboard.no.org")
- } -

diff --git a/app/views/userProfile.scala.html b/app/views/userProfile.scala.html index 2f1c8439ad..3e2a4a1d61 100644 --- a/app/views/userProfile.scala.html +++ b/app/views/userProfile.scala.html @@ -6,6 +6,7 @@ @import models.label.LabelValidationTable @(title: String, user: Option[User] = None, auditedDistance: Float)(implicit lang: Lang) @userOrg = @{UserOrgTable.getOrg(user.get.userId)} +@orgName = @{userOrg.flatMap(orgId => OrganizationTable.getOrganizationName(orgId)).getOrElse("Team Name Not Found")} @main(title) { @navbar(user, user.map(u=> "/dashboard")) @@ -17,27 +18,29 @@
diff --git a/conf/messages.de b/conf/messages.de index f41f5627a8..eb1383b285 100644 --- a/conf/messages.de +++ b/conf/messages.de @@ -406,4 +406,4 @@ create.team.title = Erstellen Sie ein neues team create.team.name = Teamname create.team.description = Teambeschreibung (optional) create.team.cancel = Stornieren -create.team.button = Team erstellen \ No newline at end of file +create.team.button = Team erstellen diff --git a/conf/messages.en b/conf/messages.en index 7287f3b6f2..765f7868cb 100644 --- a/conf/messages.en +++ b/conf/messages.en @@ -399,4 +399,4 @@ create.team.title = Create a new team create.team.name = Team name create.team.description = Team description (optional) create.team.cancel = Cancel -create.team.button = Create team \ No newline at end of file +create.team.button = Create team diff --git a/conf/messages.es b/conf/messages.es index 62762a2668..0366f785d8 100644 --- a/conf/messages.es +++ b/conf/messages.es @@ -409,4 +409,4 @@ create.team.title = Crear un nuevo equipo create.team.name = Nombre del equipo create.team.description = Descripción del equipo (opcional) create.team.cancel = Cancelar -create.team.button = Crear equipo \ No newline at end of file +create.team.button = Crear equipo diff --git a/conf/messages.nl b/conf/messages.nl index b0327b840e..e8766b2ce5 100644 --- a/conf/messages.nl +++ b/conf/messages.nl @@ -401,4 +401,4 @@ create.team.title = Creëer een nieuw team create.team.name = Teamnaam create.team.description = Teambeschrijving (optioneel) create.team.cancel = Annuleren -create.team.button = Maak een team \ No newline at end of file +create.team.button = Maak een team diff --git a/conf/messages.zh-TW b/conf/messages.zh-TW index 3489cbbeb6..49bf03d998 100644 --- a/conf/messages.zh-TW +++ b/conf/messages.zh-TW @@ -65,6 +65,7 @@ city.name.pittsburgh-pa = 匹茲堡 city.name.chicago-il = 芝加哥 city.name.amsterdam = 阿姆斯特丹 city.name.la-piedad = 拉彼達 +city.name.la-piedad-old = 拉彼達(old) city.name.oradell-nj = 奧拉德爾 city.name.zurich = 蘇黎世 city.name.taipei = 臺北市 @@ -333,7 +334,6 @@ results.legend.high = 高 results.legend.low = 低 results.legend.body = 當鄰里顯示灰色時,代表目前缺乏足夠的數據來評估人行道問題。請點擊任何一個鄰里協助標記!使用我們的API - dashboard.current.team.greeting = 團隊: {0} dashboard.leave.team = 離開隊伍:{0} dashboard.create.team = 創建團隊 @@ -417,6 +417,7 @@ routebuilder.delete.route = 刪除路線 routebuilder.saved.icon.alt = 一個有白色勾號在中心的綠色圓圈 routebuilder.saved = 路線已儲存! routebuilder.explore.route = 探索此路線 +routebuilder.view.in.labelmap = 標記地圖中查看 routebuilder.build.another.route = 設立另一條路線 routebuilder.share.title = 儲存此連結供未來使用: routebuilder.copy.link = 複製連結 @@ -436,4 +437,4 @@ create.team.title = 創建一個新團隊 create.team.name = 隊名 create.team.description = 團隊描述(可選) create.team.cancel = 取消 -create.team.button = 創建團隊 \ No newline at end of file +create.team.button = 創建團隊 diff --git a/public/javascripts/Progress/src/Progress.js b/public/javascripts/Progress/src/Progress.js index 528f7a3f8d..8bd8ab3953 100644 --- a/public/javascripts/Progress/src/Progress.js +++ b/public/javascripts/Progress/src/Progress.js @@ -109,4 +109,4 @@ function Progress (_, $, userRole) { $('.put-user-org').on('click', putUserTeam); $('#save-team-button').on('click', createTeam); -} \ No newline at end of file +} From 2feb00274b7ad1ade67d7a8e73b9e7a42d7335d0 Mon Sep 17 00:00:00 2001 From: aslassi777 Date: Thu, 31 Oct 2024 13:33:11 -0700 Subject: [PATCH 05/36] Making small refactoring changes to code, but most importantly making changes to logging to ensure joining, creating and leaving teams works --- app/controllers/UserProfileController.scala | 6 +++--- app/models/user/OrganizationTable.scala | 4 ++-- app/views/leaderboard.scala.html | 2 +- app/views/userProfile.scala.html | 10 +++++----- conf/messages.de | 11 ++++++----- conf/messages.en | 11 ++++++----- conf/messages.es | 11 ++++++----- conf/messages.nl | 11 ++++++----- conf/messages.zh-TW | 12 ++++++------ conf/routes | 2 +- public/javascripts/Progress/src/Progress.js | 15 +++++---------- 11 files changed, 47 insertions(+), 48 deletions(-) diff --git a/app/controllers/UserProfileController.scala b/app/controllers/UserProfileController.scala index 2d57b8dff0..ac6727213f 100644 --- a/app/controllers/UserProfileController.scala +++ b/app/controllers/UserProfileController.scala @@ -186,10 +186,10 @@ class UserProfileController @Inject() (implicit val env: Environment[User, Sessi val userId: UUID = user.userId if (user.role.getOrElse("") != "Anonymous") { val userOrg: Option[Int] = UserOrgTable.getOrg(userId) - if (userOrg.headOption.isEmpty) { + if (userOrg.isEmpty) { UserOrgTable.save(userId, orgId) - } else if (userOrg.head != orgId) { - UserOrgTable.remove(userId, userOrg.head) + } else if (userOrg.get != orgId) { + UserOrgTable.remove(userId, userOrg.get) UserOrgTable.save(userId, orgId) } } diff --git a/app/models/user/OrganizationTable.scala b/app/models/user/OrganizationTable.scala index 1d7f9604e3..e9af3841af 100644 --- a/app/models/user/OrganizationTable.scala +++ b/app/models/user/OrganizationTable.scala @@ -67,7 +67,7 @@ object OrganizationTable { * @return The auto-generated ID of the newly created organization. */ def insert(orgName: String, orgDescription: String): Int = db.withSession { implicit session => - val newOrganization = Organization(0, orgName, orgDescription) // orgId is auto-generated. - (organizations returning organizations.map(_.orgId)) += newOrganization + val newOrganization = Organization(0, orgName, orgDescription) // orgId is auto-generated. + (organizations returning organizations.map(_.orgId)) += newOrganization } } diff --git a/app/views/leaderboard.scala.html b/app/views/leaderboard.scala.html index 43535239b6..7c43c80acb 100644 --- a/app/views/leaderboard.scala.html +++ b/app/views/leaderboard.scala.html @@ -242,7 +242,7 @@

@Messages("leaderboard.weekly.title @if(user && user.get.role.getOrElse("") != "Anonymous" && !UserOrgTable.getOrg(user.get.userId).isEmpty) {

@Messages("leaderboard.org.title", OrganizationTable.getOrganizationName(UserOrgTable.getOrg(user.get.userId).get))

-
@Messages("leaderboard.org.detail", OrganizationTable.getOrganizationName(UserOrgTable.getOrg(user.get.userId).head).getOrElse(""))
+
@Messages("leaderboard.org.detail", OrganizationTable.getOrganizationName(UserOrgTable.getOrg(user.get.userId).get).getOrElse(""))

diff --git a/app/views/userProfile.scala.html b/app/views/userProfile.scala.html index 3e2a4a1d61..e24c6f5a85 100644 --- a/app/views/userProfile.scala.html +++ b/app/views/userProfile.scala.html @@ -52,7 +52,7 @@ + + + + + + + + + + + + + + + + + + + +
@Messages("audited.street")
@Messages("validated.correct")
@Messages("validated.incorrect")
@Messages("unsure.caps")
@Messages("unvalidated")
@@ -208,7 +228,7 @@

@Messages("dashboard.mistakes.header")

i18next.use(i18nextHttpBackend).init({ backend: { loadPath: '/assets/locales/{{lng}}/{{ns}}.json' }, fallbackLng: 'en', - ns: ['dashboard', 'common'], + ns: ['dashboard', 'common', 'labelmap'], defaultNS: 'dashboard', lng: "@lang.code", debug: false diff --git a/public/javascripts/Admin/src/Admin.GSVLabelView.js b/public/javascripts/Admin/src/Admin.GSVLabelView.js index ef95cf38e2..e266f314ef 100644 --- a/public/javascripts/Admin/src/Admin.GSVLabelView.js +++ b/public/javascripts/Admin/src/Admin.GSVLabelView.js @@ -188,24 +188,45 @@ function AdminGSVLabelView(admin, source) { } self.prevAction = null; - self.agreeButton.click(function() { - if (self.prevAction !== "Agree") { - _disableValidationButtons(); - _validateLabel("Agree"); - } - }); - self.disagreeButton.click(function() { - if (self.prevAction !== "Disagree") { - _disableValidationButtons(); - _validateLabel("Disagree"); - } - }); - self.unsureButton.click(function() { - if (self.prevAction !== "Unsure") { - _disableValidationButtons(); - _validateLabel("Unsure"); - } - }); + self.commentButton = self.modal.find("#comment-button"); + self.commentTextArea = self.modal.find("#comment-textarea"); + + // Hide validation buttons and comment submission for images labeled by current user + if (self.source === "UserMap") { + self.modal.find('#validation-input-holder h3').hide(); + _hideValidationButtons(); + self.commentButton.hide(); + self.commentTextArea.hide(); + } else { + self.agreeButton.click(function () { + if (self.prevAction !== "Agree") { + _disableValidationButtons(); + _validateLabel("Agree"); + } + }); + self.disagreeButton.click(function () { + if (self.prevAction !== "Disagree") { + _disableValidationButtons(); + _validateLabel("Disagree"); + } + }); + self.unsureButton.click(function () { + if (self.prevAction !== "Unsure") { + _disableValidationButtons(); + _validateLabel("Unsure"); + } + }); + + self.commentButton.popover({ + template : '' + }); + self.commentButton.click(function() { + var comment = self.commentTextArea.val(); + if (comment) { + _submitComment(comment); + } + }); + } self.lowQualityButton.click(function() { _setFlag("low_quality", !self.flags["low_quality"]); @@ -217,18 +238,6 @@ function AdminGSVLabelView(admin, source) { _setFlag("stale", !self.flags["stale"]); }); - self.commentButton = self.modal.find("#comment-button"); - self.commentTextArea = self.modal.find("#comment-textarea"); - self.commentButton.popover({ - template : '' - }); - self.commentButton.click(function() { - var comment = self.commentTextArea.val(); - if (comment) { - _submitComment(comment); - } - }); - self.modalTitle = self.modal.find("#myModalLabel"); self.modalTimestamp = self.modal.find("#timestamp"); self.modalLabelTypeValue = self.modal.find("#label-type-value"); @@ -343,6 +352,13 @@ function AdminGSVLabelView(admin, source) { } } } + function _hideValidationButtons() { + for (var button in self.resultButtons) { + if (self.resultButtons.hasOwnProperty(button)) { + self.resultButtons[button].hide(); + } + } + } /** * Creates the validation row text and displays it in the label. diff --git a/public/javascripts/Admin/src/Admin.js b/public/javascripts/Admin/src/Admin.js index 808a068445..0b78dc17b4 100644 --- a/public/javascripts/Admin/src/Admin.js +++ b/public/javascripts/Admin/src/Admin.js @@ -209,7 +209,7 @@ function Admin(_, $) { } toggleLabelLayer(checkbox.id.split('-')[0], checkbox, slider, map, mapData); } else if (checkbox.getAttribute('data-filter-type') === 'label-validations') { - filterLabelLayers(checkbox, map, mapData); + filterLabelLayers(checkbox, map, mapData, true); } else { filterStreetLayer(map); } diff --git a/public/javascripts/PSMap/AddLabelsToMap.js b/public/javascripts/PSMap/AddLabelsToMap.js index 347bba3840..6671039ad6 100644 --- a/public/javascripts/PSMap/AddLabelsToMap.js +++ b/public/javascripts/PSMap/AddLabelsToMap.js @@ -53,9 +53,19 @@ function AddLabelsToMap(map, labelData, params) { mapData.sortedLabels['Crosswalk'].map(l => l.length).reduce((acc, len) => acc + len, 0); document.getElementById('td-number-of-signals').innerHTML = mapData.sortedLabels['Signal'].map(l => l.length).reduce((acc, len) => acc + len, 0); + + filterLabelLayers('incorrect', map, mapData, false); } else { - // For LabelMap. Set up the initial set of filters. - filterLabelLayers('incorrect', map, mapData); + // For LabelMap. + document.getElementById('map-legend-other').innerHTML = + ""; + document.getElementById('map-legend-occlusion').innerHTML = + ""; + + // Set up the initial set of filters. + filterLabelLayers('incorrect', map, mapData, true); } // Set up the label hover and popup functionality. diff --git a/public/javascripts/PSMap/PSMapUtilities.js b/public/javascripts/PSMap/PSMapUtilities.js index 03ba40c494..93e5c121c0 100644 --- a/public/javascripts/PSMap/PSMapUtilities.js +++ b/public/javascripts/PSMap/PSMapUtilities.js @@ -39,13 +39,13 @@ function toggleLabelLayer(labelType, checkbox, slider, map, mapData) { * @param map The Mapbox map object. * @param mapData */ -function filterLabelLayers(checkbox, map, mapData) { +function filterLabelLayers(checkbox, map, mapData, labelMap) { if (checkbox) mapData[checkbox.id] = checkbox.checked; Object.keys(mapData.layerNames).forEach(function (key) { for (let i = 0; i < mapData.layerNames[key].length; i++) { - map.setFilter(mapData.layerNames[key][i], [ + // Create the base filter + let filter = [ 'all', - ['any', mapData.lowQualityUsers, ['==', ['get', 'high_quality_user'], true]], [ 'any', ['all', mapData.correct, ['==', ['get', 'correct'], true]], @@ -53,7 +53,15 @@ function filterLabelLayers(checkbox, map, mapData) { ['all', mapData.unsure, ['==', ['get', 'correct'], null], ['==', ['get', 'has_validations'], true]], ['all', mapData.unvalidated, ['==', ['get', 'correct'], null], ['==', ['get', 'has_validations'], false]] ] - ]); + ]; + + // Conditionally add the high_quality_user filter if labelMap is true + if (labelMap) { + filter.push(['any', mapData.lowQualityUsers, ['==', ['get', 'high_quality_user'], true]]); + } + + // Apply the filter to the map layer + map.setFilter(mapData.layerNames[key][i], filter); } }); } diff --git a/public/javascripts/Progress/src/Progress.js b/public/javascripts/Progress/src/Progress.js index 01391af11d..8f304ebc41 100644 --- a/public/javascripts/Progress/src/Progress.js +++ b/public/javascripts/Progress/src/Progress.js @@ -12,13 +12,17 @@ function Progress (_, $, userRole) { neighborhoodTooltip: 'completionRate', neighborhoodFillColor: '#5d6d6b', neighborhoodFillOpacity: 0.1, + popupLabelViewer: AdminGSVLabelView(false, "UserMap"), includeLabelCounts: true }; + var self = {} CreatePSMap($, params).then(m => { - window.map = m[0]; - setRegionFocus(window.map); + self.map = m[0]; + self.mapData = m[3]; + setRegionFocus(self.map); + addLegendListeners(self.map, self.mapData); }); - + window.map = self; // Get total reward if a turker. if (userRole === 'Turker') { $.ajax({ @@ -76,5 +80,15 @@ function Progress (_, $, userRole) { }); } + function addLegendListeners(map, mapData) { + // Add listeners on the checkboxes. + $('#map-label-legend tr input[type="checkbox"]').each(function () { + $(this).on('click', () => { + filterLabelLayers(this, map, mapData, false); + }); + this.disabled = false; // Enable the checkbox now that the map has loaded. + }); + } + $('.put-user-org').on('click', putUserOrg); } diff --git a/public/stylesheets/userProfile.css b/public/stylesheets/userProfile.css index b2dea122fe..b7a8803d18 100644 --- a/public/stylesheets/userProfile.css +++ b/public/stylesheets/userProfile.css @@ -18,6 +18,7 @@ svg { #map-label-legend table td { border-top: 0; + padding: 2px; } #profile-container { From fc35eebe50b60091a207f7e764edd1bfac5ab0a9 Mon Sep 17 00:00:00 2001 From: leejoh22 Date: Thu, 7 Nov 2024 18:40:43 -0800 Subject: [PATCH 07/36] Implemented PR feedback, added label filter to admin user dashboard view --- Gruntfile.js | 1 - app/controllers/AdminController.scala | 4 +++- app/views/admin/user.scala.html | 20 +++++++++++++++++++ app/views/userProfile.scala.html | 10 +++++----- .../Admin/src/Admin.GSVLabelView.js | 15 +++++--------- public/javascripts/Admin/src/Admin.User.js | 16 ++++++++++++++- public/javascripts/PSMap/AddLabelsToMap.js | 10 ---------- public/javascripts/PSMap/PSMapUtilities.js | 10 +++++----- 8 files changed, 53 insertions(+), 33 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 4855859764..6184ea2896 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -42,7 +42,6 @@ module.exports = function(grunt) { 'public/javascripts/Admin/src/*.js', 'public/javascripts/SVValidate/src/util/*.js', 'public/javascripts/common/Panomarker.js', - 'public/javascripts/common/UtilitiesPanomarker.js', 'public/javascripts/Progress/src/*.js', 'public/javascripts/common/Utilities.js', 'public/javascripts/common/UtilitiesSidewalk.js', diff --git a/app/controllers/AdminController.scala b/app/controllers/AdminController.scala index 8c77f3d416..488aca3694 100644 --- a/app/controllers/AdminController.scala +++ b/app/controllers/AdminController.scala @@ -275,7 +275,9 @@ class AdminController @Inject() (implicit val env: Environment[User, SessionAuth "audit_task_id" -> label.auditTaskId, "label_id" -> label.labelId, "gsv_panorama_id" -> label.gsvPanoramaId, - "label_type" -> label.labelType + "label_type" -> label.labelType, + "correct" -> label.correct, + "has_validations" -> label.hasValidations ) Json.obj("type" -> "Feature", "geometry" -> point, "properties" -> properties) } diff --git a/app/views/admin/user.scala.html b/app/views/admin/user.scala.html index 79f9ef3f6e..6f04c543d0 100644 --- a/app/views/admin/user.scala.html +++ b/app/views/admin/user.scala.html @@ -54,6 +54,26 @@ @Messages("audited.street") + + + @Messages("validated.correct") + + + + + @Messages("validated.incorrect") + + + + + @Messages("unsure.caps") + + + + + @Messages("unvalidated") + + diff --git a/app/views/userProfile.scala.html b/app/views/userProfile.scala.html index ceab169457..bc3878afa9 100644 --- a/app/views/userProfile.scala.html +++ b/app/views/userProfile.scala.html @@ -80,22 +80,22 @@ @Messages("audited.street") - + @Messages("validated.correct") - + @Messages("validated.incorrect") - + - + @Messages("unsure.caps") - + @Messages("unvalidated") diff --git a/public/javascripts/Admin/src/Admin.GSVLabelView.js b/public/javascripts/Admin/src/Admin.GSVLabelView.js index e266f314ef..1a42962361 100644 --- a/public/javascripts/Admin/src/Admin.GSVLabelView.js +++ b/public/javascripts/Admin/src/Admin.GSVLabelView.js @@ -25,7 +25,7 @@ function AdminGSVLabelView(admin, source) { '' + '