Skip to content

Rating color and fix's to Member Profile & Stats #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@ The following parameters can be set in config files or in env variables:
- ES: config object for Elasticsearch
- ES.HOST: Elasticsearch host
- ES.API_VERSION: Elasticsearch API version
- ES.ES_INDEX: Elasticsearch index name for member
- ES.ES_TYPE: Elasticsearch index type for member
- ES.MEMBER_PROFILE_ES_INDEX: Elasticsearch index name for member profile
- ES.MEMBER_PROFILE_ES_TYPE: Elasticsearch index type for member profile
- ES.MEMBER_TRAIT_ES_INDEX: Elasticsearch index name for member trait
- ES.MEMBER_TRAIT_ES_TYPE: Elasticsearch index type for member trait
- ES.MEMBER_STATS_ES_INDEX: Elasticsearch index name for member stats
- ES.MEMBER_STATS_ES_TYPE: Elasticsearch index type for member stats
- ES.MEMBER_SKILLS_ES_INDEX: Elasticsearch index name for member skills
- ES.MEMBER_SKILLS_ES_TYPE: Elasticsearch index type for member skills
- FILE_UPLOAD_SIZE_LIMIT: the file upload size limit in bytes
- PHOTO_URL_TEMPLATE: photo URL template, its <key> will be replaced with S3 object key
- VERIFY_TOKEN_EXPIRATION: verify token expiration in minutes
Expand Down
3 changes: 2 additions & 1 deletion app-bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ global.Promise = require('bluebird')
const Joi = require('joi')

Joi.page = () => Joi.number().integer().min(1).default(1)
Joi.perPage = () => Joi.number().integer().min(1).max(100).default(20)
Joi.perPage = () => Joi.number().integer().min(1).max(100).default(10)
Joi.sort = () => Joi.string().default("asc")
8 changes: 5 additions & 3 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ module.exports = {
HOST: process.env.ES_HOST || 'localhost:9200',
API_VERSION: process.env.ES_API_VERSION || '6.8',
// member index
ES_INDEX: process.env.ES_INDEX || 'members-2020-01',
MEMBER_PROFILE_ES_INDEX: process.env.MEMBER_PROFILE_ES_INDEX || 'members-2020-01',
// member type, ES 6.x accepts only 1 Type per index and it's mandatory to define it
ES_TYPE: process.env.ES_TYPE || 'profiles',
MEMBER_PROFILE_ES_TYPE: process.env.MEMBER_PROFILE_ES_TYPE || 'profiles',
MEMBER_TRAIT_ES_INDEX: process.env.MEMBER_TRAIT_ES_INDEX || 'members-2020-01',
MEMBER_TRAIT_ES_TYPE: process.env.MEMBER_TRAIT_ES_TYPE || 'profiletraits',
MEMBER_STATS_ES_INDEX: process.env.MEMBER_STATS_ES_INDEX || 'memberstats-2020-01',
MEMBER_STATS_ES_TYPE: process.env.MEMBER_STATS_ES_TYPE || 'stats'
MEMBER_STATS_ES_TYPE: process.env.MEMBER_STATS_ES_TYPE || 'stats',
MEMBER_SKILLS_ES_INDEX: process.env.MEMBER_SKILLS_ES_INDEX || 'memberskills-2020-01',
MEMBER_SKILLS_ES_TYPE: process.env.MEMBER_SKILLS_ES_TYPE || 'skills'
},

// health check timeout in milliseconds
Expand Down
29 changes: 28 additions & 1 deletion src/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,24 @@ const uuid = require('uuid/v4')
const querystring = require('querystring')
const request = require('request');

// Color schema for Ratings
const RATING_COLORS = [{
color: '#9D9FA0' /* Grey */,
limit: 900,
}, {
color: '#69C329' /* Green */,
limit: 1200,
}, {
color: '#616BD5' /* Blue */,
limit: 1500,
}, {
color: '#FCD617' /* Yellow */,
limit: 2200,
}, {
color: '#EF3A3A' /* Red */,
limit: Infinity,
}];

// Bus API Client
let busApiClient

Expand Down Expand Up @@ -478,6 +496,8 @@ function cleanUpStatistics (stats, fields) {
if (typeof stats[count].maxRating == "string") {
stats[count].maxRating = JSON.parse(stats[count].maxRating)
}
// set the rating color
stats[count].maxRating.ratingColor = this.getRatingColor(stats[count].maxRating.rating)
}
if (stats[count].hasOwnProperty("DATA_SCIENCE")) {
if (typeof stats[count].DATA_SCIENCE == "string") {
Expand Down Expand Up @@ -602,6 +622,12 @@ function findTagById (data, id) {
return _.find(data, { 'id': id });
}

function getRatingColor(rating) {
let i = 0; const r = Number(rating);
while (RATING_COLORS[i].limit <= r) i += 1;
return RATING_COLORS[i].color || 'black';
}

module.exports = {
wrapExpress,
autoWrapExpress,
Expand All @@ -625,5 +651,6 @@ module.exports = {
cleanupSkills,
mergeSkills,
getAllTags,
findTagById
findTagById,
getRatingColor
}
14 changes: 7 additions & 7 deletions src/common/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,16 @@ logger.decorateWithLogging = (service) => {
const params = method.params || getParams(method)
service[name] = async function () {
logger.debug(`ENTER ${name}`)
logger.debug('input arguments')
const args = Array.prototype.slice.call(arguments)
logger.debug(util.inspect(_sanitizeObject(_combineObject(params, args))))
// logger.debug('input arguments')
// const args = Array.prototype.slice.call(arguments)
// logger.debug(util.inspect(_sanitizeObject(_combineObject(params, args))))
try {
const result = await method.apply(this, arguments)
logger.debug(`EXIT ${name}`)
logger.debug('output arguments')
if (result !== null && result !== undefined) {
logger.debug(util.inspect(_sanitizeObject(result)))
}
// logger.debug('output arguments')
// if (result !== null && result !== undefined) {
// logger.debug(util.inspect(_sanitizeObject(result)))
// }
return result
} catch (e) {
logger.logFullError(e, name)
Expand Down
14 changes: 7 additions & 7 deletions src/init-es.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ const client = helper.getESClient()

const initES = async () => {
if (process.argv.length === 3 && process.argv[2] === 'force') {
logger.info(`Delete index ${config.ES.ES_INDEX} if any.`)
logger.info(`Delete index ${config.ES.MEMBER_PROFILE_ES_INDEX} if any.`)
try {
await client.indices.delete({ index: config.ES.ES_INDEX })
await client.indices.delete({ index: config.ES.MEMBER_PROFILE_ES_INDEX })
} catch (err) {
// ignore
}
Expand All @@ -28,14 +28,14 @@ const initES = async () => {
}
}

let exists = await client.indices.exists({ index: config.ES.ES_INDEX })
let exists = await client.indices.exists({ index: config.ES.MEMBER_PROFILE_ES_INDEX })
if (exists) {
logger.info(`The index ${config.ES.ES_INDEX} exists.`)
logger.info(`The index ${config.ES.MEMBER_PROFILE_ES_INDEX} exists.`)
} else {
logger.info(`The index ${config.ES.ES_INDEX} will be created.`)
logger.info(`The index ${config.ES.MEMBER_PROFILE_ES_INDEX} will be created.`)

const body = { mappings: {} }
body.mappings[config.get('ES.ES_TYPE')] = {
body.mappings[config.get('ES.MEMBER_PROFILE_ES_TYPE')] = {
properties: {
handleLower: { type: 'keyword' },
handle: { type: 'keyword' },
Expand All @@ -45,7 +45,7 @@ const initES = async () => {
}

await client.indices.create({
index: config.ES.ES_INDEX,
index: config.ES.MEMBER_PROFILE_ES_INDEX,
body
})
}
Expand Down
17 changes: 9 additions & 8 deletions src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ module.exports = {
method: 'checkHealth'
}
},
'/members/search/members': {
get: {
controller: 'SearchController',
method: 'searchMembers',
auth: 'jwt',
allowNoToken: true,
scopes: [MEMBERS.READ, MEMBERS.ALL]
}
},
'/members/:handle': {
get: {
controller: 'MemberController',
Expand Down Expand Up @@ -117,14 +126,6 @@ module.exports = {
controller: 'MiscController',
method: 'getMemberFinancial',
auth: 'jwt',
scopes: [MEMBERS.READ, MEMBERS.ALL]
}
},
'/members': {
get: {
controller: 'SearchController',
method: 'searchMembers',
auth: 'jwt',
allowNoToken: true,
scopes: [MEMBERS.READ, MEMBERS.ALL]
}
Expand Down
4 changes: 2 additions & 2 deletions src/scripts/seed-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,8 @@ async function seedData () {
await helper.create('Member', member)
// create member in ES
await esClient.create({
index: config.ES.ES_INDEX,
type: config.ES.ES_TYPE,
index: config.ES.MEMBER_PROFILE_ES_INDEX,
type: config.ES.MEMBER_PROFILE_ES_TYPE,
id: member.handleLower,
body: member,
refresh: 'true' // refresh ES so that it is visible for read operations instantly
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/view-es-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const esClient = helper.getESClient()
async function showESData () {
const result = await esClient.search({
index: indexName,
type: config.get('ES.ES_TYPE') // type name is same for all indices
type: config.get('ES.MEMBER_PROFILE_ES_TYPE') // type name is same for all indices
})
return result.hits.hits || []
}
Expand Down
26 changes: 21 additions & 5 deletions src/services/MemberService.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ const uuid = require('uuid/v4')
const config = require('config')
const helper = require('../common/helper')
const logger = require('../common/logger')
const statisticsService = require('./StatisticsService')
const errors = require('../common/errors')
const constants = require('../../app-constants')
const HttpStatus = require('http-status-codes')

const esClient = helper.getESClient()

const MEMBER_FIELDS = ['userId', 'handle', 'handleLower', 'firstName', 'lastName', 'tracks', 'status',
'addresses', 'description', 'email', 'homeCountryCode', 'competitionCountryCode', 'photoURL', 'createdAt',
'createdBy','updatedAt','updatedBy']
'addresses', 'description', 'email', 'homeCountryCode', 'competitionCountryCode', 'photoURL', 'maxRating',
'createdAt', 'createdBy','updatedAt','updatedBy']

const INTERNAL_MEMBER_FIELDS = ['newEmail', 'emailVerifyToken', 'emailVerifyTokenDate', 'newEmailVerifyToken',
'newEmailVerifyTokenDate', 'handleSuggest']
Expand Down Expand Up @@ -58,11 +59,11 @@ function omitMemberAttributes (currentUser, mb) {
*/
async function getMember (currentUser, handle, query) {
// validate and parse query parameter
const selectFields = helper.parseCommaSeparatedString(query.fields, MEMBER_FIELDS)
const selectFields = helper.parseCommaSeparatedString(query.fields, MEMBER_FIELDS) || MEMBER_FIELDS
// query member from Elasticsearch
const esQuery = {
index: config.ES.ES_INDEX,
type: config.ES.ES_TYPE,
index: config.ES.MEMBER_PROFILE_ES_INDEX,
type: config.ES.MEMBER_PROFILE_ES_TYPE,
size: constants.ES_SEARCH_MAX_SIZE, // use a large size to query all records
body: {
query: {
Expand All @@ -80,6 +81,21 @@ async function getMember (currentUser, handle, query) {
} else {
members = _.map(members.hits.hits, '_source')
}
// get the 'maxRating' from stats
if (_.includes(selectFields, 'maxRating')) {
for (let i = 0; i < members.length; i += 1) {
const memberStatsFields = { "fields": "userId,groupId,handleLower,maxRating" }
const memberStats = await statisticsService.getMemberStats(currentUser, members[i].handleLower,
memberStatsFields, false)
if(memberStats[0]) {
if (memberStats[0].hasOwnProperty("maxRating")) {
members[i].maxRating = memberStats[0].maxRating
} else {
members[i].maxRating = {}
}
}
}
}
// clean member fields according to current user
members = cleanMember(currentUser, members)
// select fields
Expand Down
5 changes: 2 additions & 3 deletions src/services/MemberTraitService.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async function getTraits (currentUser, handle, query) {
// get member
const member = await helper.getMemberByHandle(handle)
// parse query parameters
const traitIds = helper.parseCommaSeparatedString(query.traitIds, TRAIT_IDS)
const traitIds = helper.parseCommaSeparatedString(query.traitIds, TRAIT_IDS) || TRAIT_IDS
const fields = helper.parseCommaSeparatedString(query.fields, TRAIT_FIELDS) || TRAIT_FIELDS

// query member traits from Elasticsearch
Expand Down Expand Up @@ -203,8 +203,7 @@ updateTraits.schema = createTraits.schema
*/
async function removeTraits (currentUser, handle, query) {
// parse trait ids
const traitIds = helper.parseCommaSeparatedString(query.traitIds, TRAIT_IDS)

const traitIds = helper.parseCommaSeparatedString(query.traitIds, TRAIT_IDS) || TRAIT_IDS
const member = await helper.getMemberByHandle(handle)
// check authorization
if (!helper.canManageMember(currentUser, member)) {
Expand Down
25 changes: 13 additions & 12 deletions src/services/MiscService.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,21 @@ const MEMBER_FINANCIAL_FIELDS = ['userId', 'amount', 'status', 'createdAt', 'upd
*/
async function getMemberFinancial (currentUser, handle, query) {
// validate and parse query parameter
const fields = helper.parseCommaSeparatedString(query.fields, MEMBER_FINANCIAL_FIELDS)
const fields = helper.parseCommaSeparatedString(query.fields, MEMBER_FINANCIAL_FIELDS) || MEMBER_FINANCIAL_FIELDS
// get member by handle
const member = await helper.getMemberByHandle(handle)
// only admin, M2M or user himself can get financial data
if (!helper.canManageMember(currentUser, member)) {
throw new errors.ForbiddenError('You are not allowed to get financial data of the user.')
}
// get financial data by member user id
let data = await helper.getEntityByHashKey(handle, 'MemberFinancial', 'userId', member.userId, true)
// select fields if provided
if (fields) {
data = _.pick(data, fields)
}
return data
// // only admin, M2M or user himself can get financial data
// if (!helper.canManageMember(currentUser, member)) {
// throw new errors.ForbiddenError('You are not allowed to get financial data of the user.')
// }
// // get financial data by member user id
// let data = await helper.getEntityByHashKey(handle, 'MemberFinancial', 'userId', member.userId, true)
// // select fields if provided
// if (fields) {
// data = _.pick(data, fields)
// }
// return data
return { 'message': 'No Data' }
}

getMemberFinancial.schema = {
Expand Down
Loading