Skip to content

Commit

Permalink
Added new endpoint for observer statistics.
Browse files Browse the repository at this point in the history
  • Loading branch information
MLumme committed Aug 29, 2024
1 parent 529ac79 commit 0ec5557
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 1 deletion.
79 changes: 79 additions & 0 deletions src/main/dao/apiDao.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const urlRemover = require('../helpers/urlRemover')
const access_token = process.env.LAJI_ACCESS_TOKEN
const url_root = process.env.LAJI_API_URL
const blocklist = process.env.USER_BLOCKLIST
const name_blocklist = process.env.NAME_BLOCKLIST?.split(',') || []

class ApiDao {
axios
Expand Down Expand Up @@ -28,6 +30,83 @@ class ApiDao {
return data
}

async getObserverAggregate(birdAssociationId) {
const url = `${url_root}/warehouse/query/unit/aggregate`
const params = {
access_token: access_token,
editorOrObserverIdIsNot: blocklist,
birdAssociationAreaId: birdAssociationId,
informalTaxonGroupId: 'MVL.1',
countryId: 'ML.206',
time: '2022-01-01/2025-12-31',
collectionId: 'HR.1747,HR.3211,HR.48,HR.173',
recordQuality: 'COMMUNITY_VERIFIED,NEUTRAL,EXPERT_VERIFIED',
aggregateBy: 'gathering.team.memberName,unit.atlasClass',
atlasClass: 'MY.atlasClassEnumB,MY.atlasClassEnumC,MY.atlasClassEnumD',
selected: 'gathering.team.memberName,unit.AtlasClass',
orderBy: 'gathering.team.memberName',
cache: true,
needsCheck: false,
pageSize: 10000
}

const key = url + JSON.stringify(params)

return await this.cache.wrapper(key, async (timeout = 0) => {
const rawObserverStats = await this.getPaginatedAxios(url, params, timeout)

let observerStats = rawObserverStats.reduce((acc, stats) => {
let current = acc.length ? acc[acc.length - 1] : undefined
const memberName = stats['aggregateBy']['gathering.team.memberName']
const atlasClass = urlRemover(stats['aggregateBy']['unit.atlasClass'])
const count = stats.count

if (name_blocklist.includes(memberName?.toLowerCase())) {
return acc
}

if (!current || current.memberName !== memberName) {
current = {
memberName,
[atlasClass]: count,
total: count
}

acc.push(current)
} else {
current[atlasClass] = count
current.total = current.total + count
}

return acc
}, [])

observerStats = observerStats.sort((a, b) => {
const aCount = a.total
const bCount = b.total

if (aCount !== bCount) {
return bCount - aCount
}

const aName = a.memberName
const bName = b.memberName

if (aName < bName) {
return -1
}

if (aName > bName) {
return 1
}

return 0
})

return observerStats
})
}

async getTaxonSet(taxonSet) {
const url = `${url_root}/taxa`
const params = {
Expand Down
27 changes: 27 additions & 0 deletions src/main/domain/controllers/observer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const axios = require('axios')
const Cache = require('../../dao/cache')
const ApiDao = require('../../dao/apiDao')
const apiDao = new ApiDao(axios, new Cache())

class Observer {
/**
* Returns atlas species list.
* @returns {JSON}
*/
getObserverStats() {
return async (req, res) => {
try {
const limit = req.query.limit || 100
const birdAssociationId = req.query.birdAssociationId

const observerStats = await apiDao.getObserverAggregate(birdAssociationId)

return res.json(observerStats.slice(0, limit))
} catch (e) {
return res.status(500).send(e.message)
}
}
}
}

module.exports = Observer
8 changes: 8 additions & 0 deletions src/main/domain/routers/observerRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// eslint-disable-next-line new-cap
const observerRouter = require('express').Router()
const Observer = require('../controllers/observer')
const observer = new Observer()

observerRouter.get('/stats', observer.getObserverStats())

module.exports = observerRouter
2 changes: 2 additions & 0 deletions src/main/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ async function main() {
const taxonRouter = require('./domain/routers/taxonRouter')
const birdAssociationRouter = require('./domain/routers/birdAssociationRouter')
const healthRouter = require('./domain/routers/healthRouter')
const observerRouter = require('./domain/routers/observerRouter')

app.use(cors())

Expand All @@ -51,6 +52,7 @@ async function main() {
app.use('/api/v1/map', mapRouter)
app.use('/api/v1/taxon/', taxonRouter)
app.use('/api/v1/birdAssociation', birdAssociationRouter)
app.use('/api/v1/observer', observerRouter)
app.use('/api/v1/health', healthRouter)

async function update() {
Expand Down
13 changes: 13 additions & 0 deletions src/main/static/doc/components.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,19 @@ schemas:
$ref: '#/schemas/ActivityCategoryLookup'
MY.atlasActivityCategoryEnum5:
$ref: '#/schemas/ActivityCategoryLookup'
ObserverStats:
type: object
properties:
memberName:
type: string
total:
type: number
MY.atlasClassEnumB:
type: number
MY.atlasClassEnumC:
type: number
MY.atlasClassEnumD:
type: number
FinlandStats:
type: object
properties:
Expand Down
27 changes: 26 additions & 1 deletion src/main/static/doc/openAPI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,38 @@ paths:
tags:
- birdAssociation
responses:
200:
'200':
description: An object of bird association and its stats
content:
application/json:
schema:
type: object
$ref: 'components.yaml#/schemas/BirdAssociationStats'
/observer/stats:
get:
parameters:
- name: limit
in: query
type: number
description: Number of top observes included in response
- name: birdAssociationId
in: query
type: string
description: Id for bird association area from which top observers are wanted, if missing list for whole finland is returned.
summary: Get list of top observers and their observation stats
description: Get list of top observers and their observation stats, ordered in descending total observation order
tags:
- observer
responses:
'200':
description: List of top observers
content:
application/json:
schema:
type: array
items:
type: object
$ref: components.yaml#schemas/ObserverStats
/grid:
get:
summary: Get an object of grid stats (below ykj 740) and an array of all atlas grid squares.
Expand Down

0 comments on commit 0ec5557

Please sign in to comment.