Skip to content

Commit d03bd2c

Browse files
committed
- summarize species records
1 parent 6be628e commit d03bd2c

File tree

3 files changed

+75
-11
lines changed

3 files changed

+75
-11
lines changed

grails-app/controllers/au/org/ala/merit/ProjectController.groovy

+4-2
Original file line numberDiff line numberDiff line change
@@ -1162,13 +1162,15 @@ class ProjectController {
11621162
}
11631163

11641164
@PreAuthorise(accessLevel = 'editor')
1165-
def getSpeciesRecordsFromActivity (String activityId) {
1165+
def getSpeciesRecordsFromActivity (String activityId, String groupBy, String operator) {
11661166
if(!activityId) {
11671167
render status: HttpStatus.SC_BAD_REQUEST, text: [message: 'Activity ID must be supplied'] as JSON
11681168
return
11691169
}
11701170

1171-
render projectService.getSpeciesRecordsFromActivity(activityId) as JSON
1171+
groupBy = groupBy ?: ProjectService.DEFAULT_GROUP_BY
1172+
operator = operator ?: ProjectService.FLATTEN_BY_SUM
1173+
render projectService.getSpeciesRecordsFromActivity(activityId, groupBy, operator) as JSON
11721174
}
11731175

11741176
@PreAuthorise(accessLevel = 'editor')

grails-app/services/au/org/ala/merit/ProjectService.groovy

+60-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class ProjectService {
3838
static final String PLAN_SUBMITTED = 'submitted'
3939
static final String PLAN_UNLOCKED = 'unlocked for correction'
4040
public static final String DOCUMENT_ROLE_APPROVAL = 'approval'
41+
public static final String FLATTEN_BY_SUM = "SUM"
42+
public static final String FLATTEN_BY_COUNT = "COUNT"
43+
public static final String DEFAULT_GROUP_BY = 'scientificName,vernacularName,scientificNameID,individualsOrGroups'
4144

4245
// All projects can use the Plot Selection and Layout, Plot Description and Opportune modules, but
4346
// we don't want users recording data sets for Plot Selection and Layout so it's not included here.
@@ -2234,12 +2237,12 @@ class ProjectService {
22342237
result
22352238
}
22362239

2237-
List getSpeciesRecordsFromActivity (String activityId) {
2240+
List getSpeciesRecordsFromActivity (String activityId, String groupBy = DEFAULT_GROUP_BY, String operator = FLATTEN_BY_SUM) {
22382241
if (activityId) {
22392242
String displayFormat = 'SCIENTIFICNAME(COMMONNAME)'
22402243
String url = "${grailsApplication.config.getProperty('ecodata.baseUrl')}record/listForActivity/${activityId}"
2241-
def records = webService.getJson(url)?.records
2242-
2244+
List records = webService.getJson(url)?.records
2245+
records = groupRecords(records, groupBy, operator)
22432246
records?.each { record ->
22442247
record.species = [
22452248
scientificName: record.scientificName,
@@ -2254,4 +2257,58 @@ class ProjectService {
22542257
records
22552258
}
22562259
}
2260+
2261+
/**
2262+
* Groups records by the user defined attributes and applies the operator to the numeric values.
2263+
* @param records - dwc records
2264+
* @param groupBy - scientificName, individualsOrGroups, etc.
2265+
* @param operator - SUM
2266+
* @return
2267+
*/
2268+
List groupRecords (List records, String groupBy = DEFAULT_GROUP_BY, String operator = FLATTEN_BY_SUM) {
2269+
if (records && groupBy) {
2270+
List groupByAttributes = groupBy.tokenize(',')
2271+
// Group the records by the user defined attributes such as scientificName, individualsOrGroups, etc.
2272+
Map recordsByGroup = records.groupBy { dwcRecord ->
2273+
groupByAttributes.collect { dwcRecord[it] }
2274+
}
2275+
2276+
// For each group, summarize the records by applying the operator
2277+
Map groupsAndTheirSummary= recordsByGroup.collectEntries { groupKey, groupedRecords ->
2278+
// iterate over the records in the group and summarize them
2279+
Map summaryOfGroupedRecords = groupedRecords.inject([:], { newRecord, recordInGroup ->
2280+
// iterate over the attributes of the record and apply the operator
2281+
recordInGroup.each { dwcAttribute, dwcValue ->
2282+
// sum or count all numeric values that are not used for grouping
2283+
if (dwcAttribute !in groupByAttributes && dwcValue instanceof Number) {
2284+
switch (operator) {
2285+
case FLATTEN_BY_COUNT:
2286+
// implement count operator
2287+
break
2288+
2289+
case FLATTEN_BY_SUM:
2290+
newRecord[dwcAttribute] = (newRecord[dwcAttribute] ?: 0) + dwcValue
2291+
break
2292+
default:
2293+
log.error "Unsupported operator: ${operator}"
2294+
}
2295+
}
2296+
else {
2297+
// if not a numeric value, just copy the value to the new record
2298+
newRecord[dwcAttribute] = dwcValue
2299+
}
2300+
}
2301+
2302+
newRecord
2303+
})
2304+
2305+
// flattens groupedRecords (list) to a map
2306+
[(groupKey): summaryOfGroupedRecords]
2307+
}
2308+
2309+
return groupsAndTheirSummary.values().toList()
2310+
}
2311+
2312+
records
2313+
}
22572314
}

src/test/groovy/au/org/ala/merit/ProjectServiceSpec.groovy

+11-6
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import org.grails.web.converters.marshaller.json.MapMarshaller
1212
import org.joda.time.Period
1313
import spock.lang.Specification
1414
import spock.lang.Unroll
15-
16-
import java.util.concurrent.locks.Lock
17-
1815
/**
1916
* Tests the ProjectService class.
2017
*/
@@ -1507,13 +1504,21 @@ class ProjectServiceSpec extends Specification implements ServiceUnitTest<Projec
15071504
def "Get species records for an activity id and construct species object" (){
15081505
setup:
15091506
String activityId = 'a1'
1510-
def record = [scientificName: "sc1", vernacularName: "vn1", guid: "g1", outputSpeciesId: "o1"]
1507+
def record = [scientificName: "sc1", vernacularName: "vn1", guid: "g1", outputSpeciesId: "o1", individualCount: 1, seedMass: 5]
15111508
when:
15121509
def result = service.getSpeciesRecordsFromActivity(activityId)
15131510

15141511
then:
1515-
1 * webService.getJson( {it.contains("record/listForActivity/"+activityId)}) >> [records:[record], statusCode: HttpStatus.SC_OK]
1516-
result == [record + [species: [scientificName: "sc1", commonName: "vn1", outputSpeciesId: "o1", guid: "g1", name: "sc1 (vn1)"]]]
1512+
1 * webService.getJson( {it.contains("record/listForActivity/"+activityId)}) >> [records:[record, record], statusCode: HttpStatus.SC_OK]
1513+
result == [[scientificName: "sc1", vernacularName: "vn1", guid: "g1", outputSpeciesId: "o1", individualCount: 2, seedMass: 10] + [species: [scientificName: "sc1", commonName: "vn1", outputSpeciesId: "o1", guid: "g1", name: "sc1 (vn1)"]]]
1514+
1515+
when:
1516+
result = service.getSpeciesRecordsFromActivity(activityId, null, null)
1517+
1518+
then:
1519+
1 * webService.getJson( {it.contains("record/listForActivity/"+activityId)}) >> [records:[record, record], statusCode: HttpStatus.SC_OK]
1520+
result.size() == 2
1521+
result[0] == record + [species: [scientificName: "sc1", commonName: "vn1", outputSpeciesId: "o1", guid: "g1", name: "sc1 (vn1)"]]
15171522

15181523
}
15191524

0 commit comments

Comments
 (0)