diff --git a/gradle.properties b/gradle.properties index f64e31ca3..b16400354 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -biocollectVersion=6.8-LOGIN-SNAPSHOT +biocollectVersion=6.8-REFASSESS-SNAPSHOT grailsVersion=5.1.9 grailsGradlePluginVersion=5.1.5 assetPipelineVersion=3.3.4 diff --git a/grails-app/assets/javascripts/hubs.js b/grails-app/assets/javascripts/hubs.js index 6ee8abe78..065d98d37 100644 --- a/grails-app/assets/javascripts/hubs.js +++ b/grails-app/assets/javascripts/hubs.js @@ -480,6 +480,7 @@ function ContentViewModel(config) { self.hideBreadCrumbs = ko.observable(config.hideBreadCrumbs || false); self.hideProjectAndSurvey = ko.observable(config.hideProjectAndSurvey || false); self.hideCancelButtonOnForm = ko.observable(config.hideCancelButtonOnForm || false); + self.hideNewButtonOnRecordView = ko.observable(config.hideNewButtonOnRecordView || false); self.showNote = ko.observable(config.showNote || false); self.recordNote = ko.observable(config.recordNote || ''); self.industries = ko.observable(config.industries || false); diff --git a/grails-app/controllers/au/org/ala/biocollect/ReferenceAssessmentController.groovy b/grails-app/controllers/au/org/ala/biocollect/ReferenceAssessmentController.groovy new file mode 100644 index 000000000..b6ede6c06 --- /dev/null +++ b/grails-app/controllers/au/org/ala/biocollect/ReferenceAssessmentController.groovy @@ -0,0 +1,178 @@ +package au.org.ala.biocollect + +import au.org.ala.biocollect.merit.* +import grails.converters.JSON +import grails.web.servlet.mvc.GrailsParameterMap + +import java.time.Instant + +class ReferenceAssessmentController { + UserService userService + ProjectActivityService projectActivityService + ActivityService activityService + + + private def createAssessmentRecordFromReference(Object referenceActivity, Object assessProjectActivity, boolean deIdentify) { + def refDoc = referenceActivity.documents[0] + + def baseUrl = '' + if (!refDoc["url"].startsWith('http')) { + baseUrl = grailsApplication.config.getProperty("grails.serverURL", String) + } + + def assessPhoto = [ + licence: refDoc["licence"], + notes: refDoc["notes"], + filesize: refDoc["filesize"], + staged: true, + url: baseUrl + refDoc["url"], + filename: refDoc["filename"], + attribution: referenceActivity.outputs[0].data["imageAttribution"], + name: refDoc["name"], + documentId: '', + contentType: refDoc["contentType"], + dateTaken: refDoc["dateTaken"], + formattedSize: refDoc["formattedSize"], + thumbnailUrl: baseUrl + refDoc["thumbnailUrl"], + status: "active" + ] + + def assessActivity = [ + outputs: [ + [ + outputId: "", + outputNotCompleted: false, + data: [ + recordedBy: userService.getCurrentUserDisplayName(), + upperConditionBound: "0", + lowerConditionBound: "0", + overallConditionBestEstimate: "0", + mvgGroup: referenceActivity.outputs[0].data.vegetationStructureGroup, + huchinsonGroup: referenceActivity.outputs[0].data.huchinsonGroup, + sitePhoto: [assessPhoto], + deIdentify: deIdentify ? "Yes" : "No" + ], + name: assessProjectActivity["pActivityFormName"] + ] + ], + projectActivityId: assessProjectActivity["projectActivityId"], + userId: userService.getCurrentUserId(), + projectStage: "", + embargoed: false, + type: assessProjectActivity["pActivityFormName"], + projectId: assessProjectActivity["projectId"], + mainTheme: "" + ] + + // Create the new assessment activity record + activityService.update("", assessActivity) + + // Update the numTimesReferenced field on the reference record + referenceActivity.outputs[0].data.numTimesReferenced = + referenceActivity.outputs[0].data.numTimesReferenced as Integer + 1 + activityService.update(referenceActivity.activityId, referenceActivity) + + // Return the assessment activity + assessActivity + } + + def requestRecords() { + def config = grailsApplication.config.getProperty("refAssess", Map) + def body = request.JSON + def result + + // Ensure BioCollect is configured for reference assessment projects + if (!config) { + response.status = 500 + result = [message: 'The application is not configured for reference assessment projects'] + render result as JSON + return + } + + // Ensure the body of the request contains the required fields + if (!body['vegetationStructureGroups'] || !body['climateGroups'] || !body.keySet().contains('deIdentify')) { + response.status = 400 + result = [message: 'Please ensure the assessment record request contains all relevant fields'] + render result as JSON + return + } + + // Ensure the user is authenticated + if (!userService.getCurrentUserId()) { + response.status = 403 + result = [message: 'User is not authenticated'] + render result as JSON + return + } + + // Get the activity records for the reference survey + def refActivitiesSearch = activityService.search([ + projectActivityId: config.reference.projectActivityId + ]) + def refActivities = refActivitiesSearch.resp.activities + def maxRecordsToCreate = config.assessment.maxRecordsToCreate as Integer + + // Ensure the reference records exist + def numRefActivities = refActivities?.size() + if (numRefActivities == 0) { + response.status = 404 + result = [message: 'No reference records found in reference survey'] + render result as JSON + return + } + + // Filter out any records without data or documents + refActivities = refActivities.findAll { + it.outputs[0].keySet().contains('data') && + it.documents.size() > 0 + } + + // Filter out reference activities by the supplied vegetation structure groups & climate groups + refActivities = refActivities.findAll { + body["vegetationStructureGroups"].contains(it.outputs[0].data["vegetationStructureGroup"]) && + body["climateGroups"].contains(it.outputs[0].data["huchinsonGroup"]) + } + + // Split & sort the reference activities into: + // Priority records (assessed <= 3 times), prioritising records assessed the MOST + // Other records (assessed > 3 times), prioritising records assessed the LEAST + + def priorityRecords = refActivities + .findAll { it.outputs[0].data.numTimesReferenced as Integer <= 3 } + .sort{ -(it.outputs[0].data.numTimesReferenced as Integer) } + def otherRecords = refActivities + .findAll { it.outputs[0].data.numTimesReferenced as Integer > 3 } + .sort{ it.outputs[0].data.numTimesReferenced as Integer } + + // Combine the two lists + refActivities = priorityRecords + otherRecords + + // Ensure there are reference records after filtering + if (refActivities.size() == 0) { + response.status = 400 + result = [message: "No reference images matching your criteria could be found."] + render result as JSON + return + } + + def assessProjectActivity = projectActivityService.get(config.assessment.projectActivityId) + def assessActivities = [] + for ( + int projectIndex = 0; + projectIndex < Math.min(maxRecordsToCreate, refActivities.size()); + projectIndex++ + ) { + assessActivities.push( + createAssessmentRecordFromReference( + refActivities[projectIndex], + assessProjectActivity, + body['deIdentify'] + ) + ) + } + + response.status = 200 + result = [message: "Found ${assessActivities.size()} images for assessment, please standby..."] + render result as JSON + } +} diff --git a/grails-app/controllers/au/org/ala/biocollect/StaticPageController.groovy b/grails-app/controllers/au/org/ala/biocollect/StaticPageController.groovy index f8b420ec4..cab571fa2 100644 --- a/grails-app/controllers/au/org/ala/biocollect/StaticPageController.groovy +++ b/grails-app/controllers/au/org/ala/biocollect/StaticPageController.groovy @@ -1,13 +1,15 @@ package au.org.ala.biocollect import au.org.ala.biocollect.merit.SettingService -import au.org.ala.web.AlaSecured +import au.org.ala.biocollect.merit.UserService +import au.org.ala.biocollect.merit.hub.HubSettings import au.org.ala.web.NoSSO import au.org.ala.web.SSO @SSO class StaticPageController { SettingService settingService + UserService userService @NoSSO def index() { String page = params.page; @@ -44,13 +46,14 @@ class StaticPageController { /** * Save static page text */ - @AlaSecured(value = ['ROLE_ADMIN']) def saveTextAreaSetting() { String text = params.textValue String settingKey = params.settingKey String returnUrl = params.returnUrl ?: g.createLink(controller: 'staticPage', action: 'index', absolute: true, params: [page: settingKey]) - if (settingKey) { + if (!userService.doesUserHaveHubRole("admin")) { + flash.errorMessage = "You do not have correct permissions to perform this action" + } else if (settingKey) { settingService.setSettingText(settingKey, text) flash.message = "Successfully saved." } else { diff --git a/grails-app/controllers/au/org/ala/biocollect/UrlMappings.groovy b/grails-app/controllers/au/org/ala/biocollect/UrlMappings.groovy index 44cd28035..dbb68b009 100644 --- a/grails-app/controllers/au/org/ala/biocollect/UrlMappings.groovy +++ b/grails-app/controllers/au/org/ala/biocollect/UrlMappings.groovy @@ -167,11 +167,14 @@ class UrlMappings { format = 'json' } + "/referenceAssessment/requestRecords"(controller: "referenceAssessment", action: [POST: "requestRecords"]) + "500"(controller:'error', action:'response500') "404"(controller:'error', action:'response404') // Following api's are used by external mobile clients + "/ws/project/search"(controller: "project", action: 'search') "/ws/survey/list/$id"(controller: "project", action: 'listSurveys') "/ws/attachment/upload"(controller: "image", action: 'upload') diff --git a/grails-app/views/admin/editHub.gsp b/grails-app/views/admin/editHub.gsp index 4f14e2409..4b1c77410 100644 --- a/grails-app/views/admin/editHub.gsp +++ b/grails-app/views/admin/editHub.gsp @@ -486,7 +486,11 @@