From df1e332a52cb5ef05c7ac86e08b211794258b132 Mon Sep 17 00:00:00 2001
From: "alex.huang"
Date: Tue, 15 Dec 2020 10:49:18 +1100
Subject: [PATCH] Feature my annotations (#391)
* initial commit
* updated API calls to get/add/delete alerts
* updated API calls to get/add/delete alerts
* use logged in user id when get/add/delete alerts
* removed useless config
* update subscribe/un-subscribe my annotations
* parameterise the alert name we want to subscribe for my annotation
* code style per code review
* init myannotations before every call
Co-authored-by: Simon Bear
---
grails-app/assets/javascripts/show.js | 51 ++++++++++++++++++-
grails-app/conf/plugin.groovy | 1 +
.../hubs/BiocacheHubsUrlMappings.groovy | 3 ++
.../biocache/hubs/OccurrenceController.groovy | 31 ++++++++++-
grails-app/i18n/messages_en.properties | 1 +
.../biocache/hubs/WebServicesService.groovy | 26 +++++++++-
.../views/occurrence/_recordSidebar.gsp | 5 ++
grails-app/views/occurrence/show.gsp | 3 +-
8 files changed, 115 insertions(+), 6 deletions(-)
diff --git a/grails-app/assets/javascripts/show.js b/grails-app/assets/javascripts/show.js
index 6ebac7dc4..03597f2ce 100644
--- a/grails-app/assets/javascripts/show.js
+++ b/grails-app/assets/javascripts/show.js
@@ -152,7 +152,6 @@ $(document).ready(function() {
alert("You can't mark a record as a duplicate of itself");
return;
} else {
-
$.post(OCC_REC.contextPath + "/occurrences/assertions/add",
{
recordUuid: recordUuid,
@@ -165,6 +164,18 @@ $(document).ready(function() {
relatedRecordReason: relatedRecordReason,
},
function (data) {
+ // when add assertion succeeds, we update alert settings
+ if (myAnnotationQueryId) {
+ var orig_state = $('#notifyChangeCheckbox').prop('data-origstate');
+ var new_state = $('#notifyChangeCheckbox').prop('checked');
+
+ // only update when user changed preference
+ if (orig_state !== new_state) {
+ var actionpath = new_state ? ("/occurrences/addAlert?queryId=" + myAnnotationQueryId) : ("/occurrences/deleteAlert?queryId=" + myAnnotationQueryId)
+ $.post(OCC_REC.contextPath + actionpath)
+ }
+ }
+
$('#assertionSubmitProgress').css({'display': 'none'});
$("#submitSuccess").html("Thanks for flagging the problem!");
$("#issueFormSubmit").hide();
@@ -197,6 +208,43 @@ $(document).ready(function() {
$(el).html(replaceURLWithHTMLLinks(html)); // convert it
});
+ var myAnnotationQueryId = null
+
+ $('#assertionButton').click(function (e) {
+ var getAlerts = OCC_REC.contextPath + "/occurrences/alerts";
+ // hide check box until we get user alerts settings
+ $("#notifyChange").hide();
+
+ $.getJSON(getAlerts, function (data) {
+ // init status
+ myAnnotationQueryId = null
+ var myAnnotationEnabled = false
+ if (data.enabledQueries) {
+ for (var i = 0; i < data.enabledQueries.length; i++) {
+ if (data.enabledQueries[i].name.indexOf(OCC_REC.alertName) !== -1) {
+ myAnnotationEnabled = true;
+ myAnnotationQueryId = data.enabledQueries[i].id
+ }
+ }
+ }
+
+ if (data.disabledQueries) {
+ for (var i = 0; i < data.disabledQueries.length; i++) {
+ if (data.disabledQueries[i].name.indexOf(OCC_REC.alertName) !== -1) {
+ myAnnotationEnabled = false;
+ myAnnotationQueryId = data.disabledQueries[i].id
+ }
+ }
+ }
+
+ // if find 'my annotation' show the check box
+ if (myAnnotationQueryId !== null) {
+ $("#notifyChange").show();
+ $("#notifyChangeCheckbox").prop('checked', myAnnotationEnabled);
+ $("#notifyChangeCheckbox").prop('data-origstate', myAnnotationEnabled);
+ }
+ })
+ })
// bind to form "close" button TODO
$("input#close").on("click", function(e) {
@@ -598,7 +646,6 @@ function updateConfirmVerificationEvents(occUuid, assertionUuid, userDisplayName
}
console.log("Submitting an assertion with userAssertionStatus: " + userAssertionStatus)
-
$.post(OCC_REC.contextPath + "/occurrences/assertions/add",
{ recordUuid: occUuid,
code: code,
diff --git a/grails-app/conf/plugin.groovy b/grails-app/conf/plugin.groovy
index 3446b61da..4b3575609 100644
--- a/grails-app/conf/plugin.groovy
+++ b/grails-app/conf/plugin.groovy
@@ -143,6 +143,7 @@ alwaysshow.imagetab = false
facets.defaultSelected = "data_resource_uid,taxon_name,year,multimedia"
+myannotation.name="My Annotations"
mapdownloads {
baseLayers {
default_layer {
diff --git a/grails-app/controllers/au/org/ala/biocache/hubs/BiocacheHubsUrlMappings.groovy b/grails-app/controllers/au/org/ala/biocache/hubs/BiocacheHubsUrlMappings.groovy
index 49f0f4e23..2e9b8b646 100644
--- a/grails-app/controllers/au/org/ala/biocache/hubs/BiocacheHubsUrlMappings.groovy
+++ b/grails-app/controllers/au/org/ala/biocache/hubs/BiocacheHubsUrlMappings.groovy
@@ -24,6 +24,9 @@ class BiocacheHubsUrlMappings {
"/occurrences/next"(controller: 'occurrence', action: 'next')
"/occurrences/previous"(controller: 'occurrence', action: 'previous')
"/occurrences/dataQualityExcludeCounts"(controller: 'occurrence', action: 'dataQualityExcludeCounts')
+ "/occurrences/alerts"(controller: 'occurrence', action: [GET: 'getAlerts'])
+ "/occurrences/addAlert"(controller: 'occurrence', action: [POST: 'addAlert'])
+ "/occurrences/deleteAlert"(controller: 'occurrence', action: [POST: 'deleteAlert'])
"/occurrences/$id"(controller: 'occurrence', action: 'show')
"/occurrence/$id"(controller: 'occurrence', action: 'show')
"/assertions/$id"(controller: 'assertions', action: 'assertions')
diff --git a/grails-app/controllers/au/org/ala/biocache/hubs/OccurrenceController.groovy b/grails-app/controllers/au/org/ala/biocache/hubs/OccurrenceController.groovy
index 4452b5db5..b31c993f6 100644
--- a/grails-app/controllers/au/org/ala/biocache/hubs/OccurrenceController.groovy
+++ b/grails-app/controllers/au/org/ala/biocache/hubs/OccurrenceController.groovy
@@ -17,7 +17,6 @@ package au.org.ala.biocache.hubs
import au.org.ala.dataquality.model.QualityProfile
import au.org.ala.web.CASRoles
-import com.google.common.base.Stopwatch
import com.maxmind.geoip2.record.Location
import grails.converters.JSON
import groovy.util.logging.Slf4j
@@ -639,4 +638,34 @@ class OccurrenceController {
data.count = qualityService.getExcludeCount(params.categoryLabel, profile.getCategories(), requestParams)
render data as JSON
}
+
+ def getAlerts() {
+ String userId = authService?.getUserId()
+ if (userId == null) {
+ response.status = 404
+ render ([error: 'userId must be supplied to get alerts'] as JSON)
+ } else {
+ render webServicesService.getAlerts(userId) as JSON
+ }
+ }
+
+ def addAlert() {
+ String userId = authService?.getUserId()
+ if (userId == null) {
+ response.status = 404
+ render ([error: 'userId must be supplied to add alert'] as JSON)
+ } else {
+ render webServicesService.addAlert(userId, params.queryId) as JSON
+ }
+ }
+
+ def deleteAlert() {
+ String userId = authService?.getUserId()
+ if (userId == null) {
+ response.status = 404
+ render ([error: 'userId must be supplied to delete alert'] as JSON)
+ } else {
+ render webServicesService.deleteAlert(userId, params.queryId) as JSON
+ }
+ }
}
diff --git a/grails-app/i18n/messages_en.properties b/grails-app/i18n/messages_en.properties
index 1c8d74379..8f6509d65 100644
--- a/grails-app/i18n/messages_en.properties
+++ b/grails-app/i18n/messages_en.properties
@@ -135,6 +135,7 @@ show.issueform.label01 = Issue type:
show.issueform.label02 = Comment:
show.issueform.label03 = Duplicate Record ID:
show.issueform.label04 = Duplicate Reason:
+show.issueform.notifyme = Notify me when records I have annotated are updated
show.issueform.relatedrecord.found.this = You are indicating this record (the one you are viewing):
show.issueform.relatedrecord.found.other = is a duplicate of this record (the id you provided):
show.issueform.button.submit = Submit
diff --git a/grails-app/services/au/org/ala/biocache/hubs/WebServicesService.groovy b/grails-app/services/au/org/ala/biocache/hubs/WebServicesService.groovy
index 1dcb6b42c..7034056c9 100644
--- a/grails-app/services/au/org/ala/biocache/hubs/WebServicesService.groovy
+++ b/grails-app/services/au/org/ala/biocache/hubs/WebServicesService.groovy
@@ -100,6 +100,21 @@ class WebServicesService {
getJsonElements(url)
}
+ def getAlerts(String userId) {
+ def url = "${grailsApplication.config.alerts.baseURL}" + "/api/alerts/user/" + userId
+ return getJsonElements(url, "${grailsApplication.config.alerts.apiKey}")
+ }
+
+ def addAlert(String userId, String queryId) {
+ String url = "${grailsApplication.config.alerts.baseURL}" + "/api/alerts/user/" + userId + "/subscribe/" + queryId
+ postFormData(url, [:], grailsApplication.config.alerts.apiKey as String)
+ }
+
+ def deleteAlert(String userId, String queryId) {
+ String url = "${grailsApplication.config.alerts.baseURL}" + "/api/alerts/user/" + userId + "/unsubscribe/" + queryId
+ postFormData(url, [:], grailsApplication.config.alerts.apiKey as String)
+ }
+
def JSONObject getDuplicateRecordDetails(JSONObject record) {
log.debug "getDuplicateRecordDetails -> ${record?.processed?.occurrence?.associatedOccurrences}"
if (record?.processed?.occurrence?.associatedOccurrences) {
@@ -401,12 +416,15 @@ class WebServicesService {
* @param url
* @return
*/
- JSONElement getJsonElements(String url) {
+ JSONElement getJsonElements(String url, String apiKey = null) {
log.debug "(internal) getJson URL = " + url
def conn = new URL(url).openConnection()
try {
conn.setConnectTimeout(10000)
conn.setReadTimeout(50000)
+ if (apiKey != null) {
+ conn.setRequestProperty('apiKey', apiKey)
+ }
return JSON.parse(conn.getInputStream(), "UTF-8")
} catch (Exception e) {
def error = "Failed to get json from web service (${url}). ${e.getClass()} ${e.getMessage()}, ${e}"
@@ -445,13 +463,17 @@ class WebServicesService {
* @param postParams
* @return postResponse (Map with keys: statusCode (int) and statusMsg (String)
*/
- def Map postFormData(String uri, Map postParams) {
+ def Map postFormData(String uri, Map postParams, String apiKey = null) {
HTTPBuilder http = new HTTPBuilder(uri)
log.debug "POST (form encoded) to ${http.uri}"
Map postResponse = [:]
http.request( Method.POST ) {
+ if (apiKey != null) {
+ headers.'apiKey' = apiKey
+ }
+
send ContentType.URLENC, postParams
response.success = { resp ->
diff --git a/grails-app/views/occurrence/_recordSidebar.gsp b/grails-app/views/occurrence/_recordSidebar.gsp
index 36479f7f2..5a2565613 100644
--- a/grails-app/views/occurrence/_recordSidebar.gsp
+++ b/grails-app/views/occurrence/_recordSidebar.gsp
@@ -351,6 +351,11 @@
+
+
+
+
+
" class="btn btn-primary" />
" class="btn btn-default" onClick="$('#loginOrFlag').modal('hide');"/>
diff --git a/grails-app/views/occurrence/show.gsp b/grails-app/views/occurrence/show.gsp
index e385bf0da..e85deb52f 100644
--- a/grails-app/views/occurrence/show.gsp
+++ b/grails-app/views/occurrence/show.gsp
@@ -59,7 +59,8 @@
status="s">'${sds}': '${grailsApplication.config.sensitiveDatasets[sds]}'${s < (sensitiveDatasets.size() - 1) ? ',' : ''}
},
- hasGoogleKey: ${grailsApplication.config.google.apikey as Boolean}
+ hasGoogleKey: ${grailsApplication.config.google.apikey as Boolean},
+ alertName: "${grailsApplication.config.myannotation.name}"
}
// Google charts