Skip to content

Commit

Permalink
Merge pull request #983 from AtlasOfLivingAustralia/feature/issue3146…
Browse files Browse the repository at this point in the history
…_master

Feature/issue3146 master
  • Loading branch information
chrisala authored Jul 1, 2024
2 parents 0b777aa + 9dde3d6 commit 99ad9d2
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 2 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ plugins {
id "com.gorylenko.gradle-git-properties" version "2.4.1"
}

version "4.6"
version "4.7-SNAPSHOT"
group "au.org.ala"
description "Ecodata"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,36 @@ class OrganisationController {
values
}

def organisationMetrics(String id) {

def organisation = Organisation.findByOrganisationId(id)

boolean approvedOnly = true

List scoreIds
Map aggregationConfig = null

Map paramData = request.JSON
if (!paramData) {
approvedOnly = params.getBoolean('approvedOnly')
scoreIds = params.getList('scoreIds')
}
else {

if (paramData.approvedOnly != null) {
approvedOnly = paramData.approvedOnly
}

scoreIds = paramData.scoreIds
aggregationConfig = paramData.aggregationConfig
}

if (organisation) {
render organisationService.organisationMetrics(id, approvedOnly, scoreIds, aggregationConfig) as JSON

} else {
render (status: 404, text: 'No such id')
}
}

}
3 changes: 3 additions & 0 deletions grails-app/domain/au/org/ala/ecodata/Activity.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Activity {
bulkImportId index: true
version false
externalIds index:true
organisationId index:true
}

static hasMany = [externalIds:ExternalId]
Expand All @@ -58,6 +59,7 @@ class Activity {
Date startDate
Date endDate
List<ExternalId> externalIds
String organisationId

/** The type of activity performed. This field must match the name of an ActivityForm */
String type
Expand Down Expand Up @@ -132,6 +134,7 @@ class Activity {
verificationStatus nullable: true, inList: ['not applicable', 'not approved', 'not verified', 'under review' , 'approved']
bulkImportId nullable: true
externalIds nullable: true
organisationId nullable: true
}

static Activity findByExternalId(String externalId) {
Expand Down
11 changes: 11 additions & 0 deletions grails-app/services/au/org/ala/ecodata/ActivityService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -645,4 +645,15 @@ class ActivityService {
}
}

List findAllForOrganisationId(id, levelOfDetail = [], includeDeleted = false) {
List activities
if (includeDeleted) {
activities = Activity.findAllByOrganisationId(id).collect {toMap(it, levelOfDetail)}
}
else {
activities = Activity.findAllByOrganisationIdAndStatus(id, ACTIVE).collect { toMap(it, levelOfDetail) }
}
activities
}

}
23 changes: 23 additions & 0 deletions grails-app/services/au/org/ala/ecodata/OrganisationService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ class OrganisationService {
public static final String PROJECTS = 'projects'

static transactional = 'mongo'
static final FLAT = 'flat'

def commonService, projectService, userService, permissionService, documentService, collectoryService, messageSource, emailService, grailsApplication
ReportingService reportingService
ActivityService activityService
ReportService reportService

def get(String id, levelOfDetail = [], includeDeleted = false) {
Organisation organisation
Expand Down Expand Up @@ -217,4 +219,25 @@ class OrganisationService {
organisationDetails
}

/**
* Returns the reportable metrics for a organisation as determined by the organisation output targets and activities
* that have been undertaken.
* @param id identifies the organisation.
* @return a Map containing the aggregated results.
*
*/
def organisationMetrics(String id, approvedOnly = false, List scoreIds = null, Map aggregationConfig = null) {
def org = Organisation.findByOrganisationId(id)
if (org) {
List toAggregate = Score.findAllByScoreIdInList(scoreIds)
List outputSummary = reportService.organisationSummary(id, toAggregate, approvedOnly, aggregationConfig) ?: []

return outputSummary
} else {
def error = "Error retrieving metrics for project - no such id ${id}"
log.error error
return [status: 'error', error: error]
}
}

}
12 changes: 12 additions & 0 deletions grails-app/services/au/org/ala/ecodata/ParatooService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,18 @@ class ParatooService {
Map projectAreaGeoJson = null
if (projectArea) {
projectAreaGeoJson = siteService.geometryAsGeoJson(projectArea)
if (projectAreaGeoJson?.type == 'Feature') {
projectAreaGeoJson = projectAreaGeoJson.geometry
}
else if (projectAreaGeoJson?.type == 'MultiPolygon') {
projectAreaGeoJson = [
type:'Polygon',
coordinates:projectAreaGeoJson.coordinates[0]
]
}
else if (projectAreaGeoJson?.type != 'Polygon') {
log.warn("Invalid geometry type for project area: ${projectAreaGeoJson?.type} specified for Monitor project ${project.projectId}")
}
}

// Monitor has users selecting a point as an approximate survey location then
Expand Down
16 changes: 16 additions & 0 deletions grails-app/services/au/org/ala/ecodata/ReportService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -526,4 +526,20 @@ class ReportService {
}
return message
}

/**
* Returns aggregated scores for a specified project.
* @param organisationId the organisation of interest.
* @param aggregationSpec defines the scores to be aggregated and if any grouping needs to occur.
* [{score:{name: , units:, aggregationType}, groupBy: {entity: <one of 'activity', 'output', 'organisation', 'site>, property: String <the entity property to group by>}, ...]
*
* @return the results of the aggregation. The results will be a List of Maps, the structure of each Map is
* described in @see au.org.ala.ecodata.reporting.Aggregation.results()
*
*/
List organisationSummary(String organisationId, List aggregationSpec, boolean approvedActivitiesOnly = false, Map topLevelAggregationConfig = null) {

List activities = activityService.findAllForOrganisationId(organisationId, 'FLAT')
aggregate(activities, aggregationSpec, approvedActivitiesOnly, topLevelAggregationConfig)
}
}
6 changes: 5 additions & 1 deletion grails-app/services/au/org/ala/ecodata/SiteService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ class SiteService {

if (!geometry) {
log.error("Invalid site: ${site.siteId} missing geometry")
return
return null
}
def result = null
switch (geometry.type) {
Expand Down Expand Up @@ -554,6 +554,10 @@ class SiteService {
break
case 'pid':
result = geometryForPid(geometry.pid)
// Spatial portal now returns results as Features.
if (result?.type == 'Feature') {
result = result.geometry
}
break
}
result
Expand Down
22 changes: 22 additions & 0 deletions src/test/groovy/au/org/ala/ecodata/ParatooServiceSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ParatooServiceSpec extends MongoSpec implements ServiceUnitTest<ParatooSer

static Map DUMMY_POLYGON = [type: 'Polygon', coordinates: [[[1, 2], [2, 2], [2, 1], [1, 1], [1, 2]]]]
static Map DUMMY_PLOT = ['type':'Point', coordinates: [1,2]]
static Map DUMMY_MULTI_POLYGON = [type: 'MultiPolygon', coordinates: [[[[1, 2], [2, 2], [2, 1], [1, 1], [1, 2]]]]]

// The am/pm in the formatted time is local dependent and this appears to be easiest way to determine the value.
String am = DateUtil.formatAsDisplayDateTime("2024-05-14T00:00:00Z")[-2..-1]
Expand Down Expand Up @@ -146,6 +147,27 @@ class ParatooServiceSpec extends MongoSpec implements ServiceUnitTest<ParatooSer

}

void "userProjects can convert a Feature or MultiPolygon typed project extent to a Polygon to support the use of known shape selection in MERIT (e.g. a NRM region)"() {
when:
List<ParatooProject> projects = service.userProjects(userId)
then:
1 * siteService.geometryAsGeoJson(_) >> DUMMY_MULTI_POLYGON
and:
projects.size() == 1
projects[0].id == "p1"
projects[0].name == "Project 1"
projects[0].accessLevel == AccessLevel.admin
projects[0].projectArea == DUMMY_POLYGON
projects[0].plots.size() == 1
projects[0].plots[0].siteId == 's2'
projects[0].protocols*.name == ["Plot Selection", "aParatooForm 1", "aParatooForm 2", "aParatooForm 3"]
}
void "The service should create a data set in the planned state when the mintCollectionId method is called"() {
setup:
ParatooCollectionId collectionId = buildCollectionId()
Expand Down

0 comments on commit 99ad9d2

Please sign in to comment.