Merge pull request #7 from mokaddem/skill_trophies

MISP Gamification - Addition of skill trophies and badges
pull/18/head
Alexandre Dulaunoy 2017-11-29 19:17:06 +01:00 committed by GitHub
commit 5a47e3bd86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 675 additions and 107 deletions

View File

@ -65,6 +65,31 @@ regularlyDays=7
[HonorBadge]
1=Has made at least one pull request on the MISP project
2=Is a donator for the MISP project
3=Has published content upvoted by the community
4=Has published valuable content for the community
5=Has published loads of valuable content for the community
[TrophyDifficulty]
#represent the % of org that can have this rank. Rank 1 is ignored as only 1 org can have it.
trophyMapping=[2, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10]
[HonorTrophy]
0=No trophy
1=Novice
2=Beginner
3=Intermediate
4=Competent
5=Experienced
6=Talented
7=Skilled
8=Advanced
9=Expert
10=Pro
11=Master
12=Grand Master
[HonorTrophyCateg]
categ=["internal_reference", "targeting_data", "antivirus_detection", "payload_delivery", "artifacts_dropped", "payload_installation", "persistence_mechanism", "network_activity", "payload_type", "attribution", "external_analysis", "financial_fraud", "support_Tool", "social_network", "person", "other" ]
[additionalInfo]
textsArray=["Proposals means either edition, acceptation or rejection", "Recent events means event aged of one month at max", "Regularly means at least one per week" ,"Heavily means at least 10 per week", "Classification means correct tagging", "The contribution rank is set such that it equals to: rank=requirement_fulfilled-requirement_not_fulfilled"]

View File

@ -22,6 +22,13 @@ class Contributor_helper:
for badgeNum in range(1, self.honorBadgeNum+1): #get Num of honorBadge
self.org_honor_badge_title[badgeNum] = self.cfg_org_rank.get('HonorBadge', str(badgeNum))
self.trophyMapping = json.loads(self.cfg_org_rank.get('TrophyDifficulty', 'trophyMapping'))
self.trophyNum = len(self.cfg_org_rank.options('HonorTrophy'))-1 #0 is not a trophy
self.categories_in_trophy = json.loads(self.cfg_org_rank.get('HonorTrophyCateg', 'categ'))
self.trophy_title = {}
for trophyNum in range(0, len(self.cfg_org_rank.options('HonorTrophy'))): #get Num of trophy
self.trophy_title[trophyNum] = self.cfg_org_rank.get('HonorTrophy', str(trophyNum))
#GLOBAL RANKING
self.org_rank_maxLevel = self.cfg_org_rank.getint('rankTitle', 'maxLevel')
self.org_rank = {}
@ -125,11 +132,19 @@ class Contributor_helper:
to_ret[i] = -1
return {'rank': final_rank, 'status': to_ret, 'totPoints': self.getOrgContributionTotalPoints(org)}
def updateOrgContributionRank(self, orgName, pnts_to_add, action, contribType, eventTime, isLabeled):
# return the awards given to the organisation
def updateOrgContributionRank(self, orgName, pnts_to_add, action, contribType, eventTime, isLabeled, categ=""):
ContributionStatus = self.getCurrentContributionStatus(orgName)
oldContributionStatus = ContributionStatus['status']
oldHonorBadges = self.getOrgHonorBadges(orgName)
oldTrophy = self.getOrgTrophies(orgName)
keyname = 'CONTRIB_ORG:{org}:{orgCateg}'
# update total points
totOrgPnts = self.serv_redis_db.incrby(keyname.format(org=orgName, orgCateg='points'), pnts_to_add)
#FIXME TEMPORARY, JUST TO TEST IF IT WORKS CORRECLTY
self.giveTrophyPointsToOrg(orgName, categ, 1)
# update date variables
if contribType == 'Attribute':
attributeWeekCount = self.serv_redis_db.incrby(keyname.format(org=orgName, orgCateg='ATTR_WEEK_COUNT'), 1)
@ -161,8 +176,6 @@ class Contributor_helper:
regularlyDays = self.regularlyDays
isRecent = (datetime.datetime.now() - eventTime).days > recentDays
print("contribType: {}, action: {}".format(contribType, action))
print("isLabeled: {}, isRecent: {}, totOrgPnts".format(isLabeled, isRecent, totOrgPnts))
#update contribution Requirement
contrib = [] #[[contrib_level, contrib_ttl], [], ...]
if totOrgPnts >= self.org_rank_requirement_pnts[1] and contribType == 'Sighting':
@ -195,11 +208,42 @@ class Contributor_helper:
if totOrgPnts >= self.org_rank_requirement_pnts[14] and contribType == 'Event' and eventWeekCount>heavilyCount and isLabeled:
contrib.append([14, util.ONE_DAY*regularlyDays])
print([r for r, ttl in contrib])
for rankReq, ttl in contrib:
self.serv_redis_db.set(keyname.format(org=orgName, orgCateg='CONTRIB_REQ_'+str(rankReq)), 1)
self.serv_redis_db.expire(keyname.format(org=orgName, orgCateg='CONTRIB_REQ_'+str(rankReq)), ttl)
ContributionStatus = self.getCurrentContributionStatus(orgName)
newContributionStatus = ContributionStatus['status']
newHonorBadges = self.getOrgHonorBadges(orgName)
newTrophy = self.getOrgTrophies(orgName)
# awards to publish
awards_given = []
for i in newContributionStatus.keys():
if oldContributionStatus[i] < newContributionStatus[i] and i != ContributionStatus['rank']:
awards_given.append(['contribution_status', i])
for badgeNum in newHonorBadges:
if badgeNum not in oldHonorBadges:
awards_given.append(['badge', badgeNum])
temp = {}
for item in oldTrophy:
categ = item['categ']
rank = item['trophy_true_rank']
temp[categ] = rank
for item in newTrophy:
categ = item['categ']
rank = item['trophy_true_rank']
try:
oldCategRank = temp[categ]
except KeyError:
oldCategRank = 0
if rank > oldCategRank:
awards_given.append(['trophy', [categ, rank]])
return awards_given
''' HONOR BADGES '''
def getOrgHonorBadges(self, org):
keyname = 'CONTRIB_ORG:{org}:{orgCateg}'
@ -218,6 +262,83 @@ class Contributor_helper:
keyname = 'CONTRIB_ORG:{org}:{orgCateg}'
self.serv_redis_db.delete(keyname.format(org=org, orgCateg='BADGE_'+str(badgeNum)))
''' TROPHIES '''
def getOrgTrophies(self, org):
self.getAllOrgsTrophyRanking()
keyname = 'CONTRIB_TROPHY:{orgCateg}'
trophy = []
for categ in self.categories_in_trophy:
key = keyname.format(orgCateg=categ)
totNum = self.serv_redis_db.zcard(key)
if totNum == 0:
continue
pos = self.serv_redis_db.zrank(key, org)
if pos is None:
continue
trophy_rank = self.posToRankMapping(pos, totNum)
trophy_Pnts = self.serv_redis_db.zscore(key, org)
trophy.append({ 'categ': categ, 'trophy_points': trophy_Pnts, 'trophy_rank': trophy_rank, 'trophy_true_rank': trophy_rank, 'trophy_title': self.trophy_title[trophy_rank]})
return trophy
def getOrgsTrophyRanking(self, categ):
keyname = 'CONTRIB_TROPHY:{orgCateg}'
res = self.serv_redis_db.zrange(keyname.format(orgCateg=categ), 0, -1, withscores=True, desc=True)
res = [[org.decode('utf8'), score] for org, score in res]
return res
def getAllOrgsTrophyRanking(self):
dico_categ = {}
for categ in self.categories_in_trophy:
res = self.getOrgsTrophyRanking(categ)
dico_categ[categ] = res
def posToRankMapping(self, pos, totNum):
mapping = self.trophyMapping
mapping_num = [math.ceil(float(float(totNum*i)/float(100))) for i in mapping]
# print(pos, totNum)
if pos == 0: #first
position = 1
else:
temp_pos = pos
counter = 1
for num in mapping_num:
if temp_pos < num:
position = counter
else:
temp_pos -= num
counter += 1
return self.trophyNum+1 - position
def giveTrophyPointsToOrg(self, org, categ, points):
keyname = 'CONTRIB_TROPHY:{orgCateg}'
self.serv_redis_db.zincrby(keyname.format(orgCateg=categ), org, points)
def removeTrophyPointsFromOrg(self, org, categ, points):
keyname = 'CONTRIB_TROPHY:{orgCateg}'
self.serv_redis_db.zincrby(keyname.format(orgCateg=categ), org, -points)
''' AWARDS HELPER '''
def getLastAwardsFromRedis(self):
date = datetime.datetime.now()
keyname = "CONTRIB_LAST_AWARDS"
prev_days = 7
topNum = self.MAX_NUMBER_OF_LAST_CONTRIBUTOR # default Num
addedOrg = []
data = []
for curDate in util.getXPrevDaysSpan(date, prev_days):
last_awards = self.getZrange(keyname, curDate, topNum)
for dico_award, sec in last_awards:
dico_award = json.loads(dico_award)
org = dico_award['org']
dic = {}
dic['orgRank'] = self.getOrgContributionRank(org)['final_rank']
dic['logo_path'] = self.getOrgLogoFromMISP(org)
dic['org'] = org
dic['epoch'] = sec
dic['award'] = dico_award['award']
data.append(dic)
return data
''' MONTHLY CONTRIBUTION '''
def getOrgPntFromRedis(self, org, date):
keyCateg = 'CONTRIB_DAY'
@ -381,7 +502,6 @@ class Contributor_helper:
prev = i
return { 'remainingPts': 0, 'stepPts': self.rankMultiplier**self.levelMax }
''' '''
''' TEST DATA '''
''' '''
@ -583,3 +703,12 @@ class Contributor_helper:
else:
honorBadge.append(0)
return honorBadge
def TEST_getOrgTrophies(self, org):
keyname = 'CONTRIB_ORG:{org}:{orgCateg}'
trophy = []
for categ in self.categories_in_trophy:
key = keyname.format(org=org, orgCateg='TROPHY_'+categ)
trophy_Pnts = random.randint(0,10)
trophy.append({'categName': categ, 'rank': trophy_Pnts})
return trophy

View File

@ -1,27 +1,41 @@
#!/usr/bin/env python3.5
import os, sys
import os, sys, json
import datetime, time
import redis
import configparser
import util
import contributor_helper
ONE_DAY = 60*60*24
configfile = os.path.join(os.environ['DASH_CONFIG'], 'config.cfg')
cfg = configparser.ConfigParser()
cfg.read(configfile)
serv_log = redis.StrictRedis(
host=cfg.get('RedisGlobal', 'host'),
port=cfg.getint('RedisGlobal', 'port'),
db=cfg.getint('RedisLog', 'db'))
serv_redis_db = redis.StrictRedis(
host=cfg.get('RedisGlobal', 'host'),
port=cfg.getint('RedisGlobal', 'port'),
db=cfg.getint('RedisDB', 'db'))
CHANNEL_LASTAWARDS = cfg.get('RedisLog', 'channelLastAwards')
chelper = contributor_helper.Contributor_helper(serv_redis_db, cfg)
def publish_log(zmq_name, name, content, channel):
to_send = { 'name': name, 'data': json.dumps(content), 'zmqName': zmq_name }
serv_log.publish(channel, json.dumps(to_send))
def printOrgInfo(org):
org_pnts = chelper.getOrgContributionTotalPoints(org)
org_c_rank = chelper.getOrgContributionRank(org)
org_c_status = chelper.getCurrentContributionStatus(org)
org_honor_badge = chelper.getOrgHonorBadges(org)
org_trophy = chelper.getOrgTrophies(org)
os.system('clear')
print()
print("Organisation points: {}".format(org_pnts))
print("Organisation contribution rank: {}".format(org_c_status['rank']))
@ -39,23 +53,38 @@ Organisation honor badges:
for badgeNum, text in chelper.org_honor_badge_title.items():
acq = 'x' if badgeNum in org_honor_badge else ' '
print("{}.\t[{}]\t{}".format(badgeNum, acq, text))
print()
print()
print('''
Organisation trophy:
--------------------------''')
for dic in org_trophy:
categ = dic['categ']
trophyRank = dic['trophy_true_rank']
trophyPnts = dic['trophy_points']
print("{}\t{} [{}]".format(categ, trophyRank, trophyPnts))
print()
def main():
if len(sys.argv) > 1:
org = sys.argv[1]
else:
org = input('Enter the organisation name: ')
printOrgInfo(org)
ContributionStatus = chelper.getCurrentContributionStatus(org)
OLD_org_c_status = ContributionStatus['status']
OLD_org_honor_badge = chelper.getOrgHonorBadges(org)
OLD_org_trophy = chelper.getOrgTrophies(org)
# ranks
while True:
org_pnts = chelper.getOrgContributionTotalPoints(org)
org_c_rank = chelper.getOrgContributionRank(org)
org_c_status = chelper.getCurrentContributionStatus(org)
org_honor_badge = chelper.getOrgHonorBadges(org)
org_trophy = chelper.getOrgTrophies(org)
userRep = input("Enter the organisation RANK to give/remove to {} (<ENTER> to finish): ".format(org))
if userRep == '':
@ -67,7 +96,7 @@ def main():
except:
print('Not an integer')
continue
if rankNum < 1 and rankNum > chelper.org_rank_maxLevel:
if rankNum < 1 or rankNum > chelper.org_rank_maxLevel:
print('Not a valid rank')
continue
@ -77,13 +106,14 @@ def main():
chelper.giveContribRankToOrg(org, rankNum)
printOrgInfo(org)
# badges
while True:
org_pnts = chelper.getOrgContributionTotalPoints(org)
org_c_rank = chelper.getOrgContributionRank(org)
org_c_status = chelper.getCurrentContributionStatus(org)
org_honor_badge = chelper.getOrgHonorBadges(org)
org_trophy = chelper.getOrgTrophies(org)
userRep = input("Enter the organisation BADGE to give/remove to {} (<ENTER> to finish): ".format(org))
if userRep == '':
@ -106,6 +136,80 @@ def main():
printOrgInfo(org)
# trophy
while True:
org_pnts = chelper.getOrgContributionTotalPoints(org)
org_c_rank = chelper.getOrgContributionRank(org)
org_c_status = chelper.getCurrentContributionStatus(org)
org_honor_badge = chelper.getOrgHonorBadges(org)
org_trophy = chelper.getOrgTrophies(org)
print()
for i, categ in enumerate(chelper.categories_in_trophy):
print("{}. {}".format(i, categ))
userCateg = input("Enter the CATEGORY in which to add/remove trophy points: ")
if userCateg == '':
break
try: #not int
userCateg = int(userCateg)
except:
print('Not an integer')
continue
if userCateg < 1 and userCateg > len(chelper.categories_in_trophy):
print('Not a valid rank')
continue
categ = chelper.categories_in_trophy[userCateg]
userRep = input("Enter the TROPHY POINTS to give/remove to {} (<ENTER> to finish) in {}: ".format(org, categ))
if userRep == '':
break
else:
# validate input
try: #not int
trophyPnts = int(userRep)
except:
print('Not an integer')
continue
chelper.giveTrophyPointsToOrg(org, categ, trophyPnts)
printOrgInfo(org)
now = datetime.datetime.now()
nowSec = int(time.time())
ContributionStatus = chelper.getCurrentContributionStatus(org)
NEW_org_c_status = ContributionStatus['status']
NEW_org_honor_badge = chelper.getOrgHonorBadges(org)
NEW_org_trophy = chelper.getOrgTrophies(org)
awards_given = []
for i in NEW_org_c_status.keys():
if OLD_org_c_status[i] < NEW_org_c_status[i] and i != ContributionStatus['rank']:
awards_given.append(['contribution_status', ContributionStatus['rank']])
for badgeNum in NEW_org_honor_badge:
if badgeNum not in OLD_org_honor_badge:
awards_given.append(['badge', badgeNum])
temp = {}
for item in OLD_org_trophy:
categ = item['categ']
rank = item['trophy_true_rank']
temp[categ] = rank
for item in NEW_org_trophy:
categ = item['categ']
rank = item['trophy_true_rank']
if rank > temp[categ]:
awards_given.append(['trophy', [categ, rank]])
for award in awards_given:
# update awards given
serv_redis_db.zadd('CONTRIB_LAST_AWARDS:'+util.getDateStrFormat(now), nowSec, json.dumps({'org': org, 'award': award, 'epoch': nowSec }))
serv_redis_db.expire('CONTRIB_LAST_AWARDS:'+util.getDateStrFormat(now), ONE_DAY*7) #expire after 7 day
# publish
publish_log('GIVE_HONOR_ZMQ', 'CONTRIBUTION', {'org': org, 'award': award, 'epoch': nowSec }, CHANNEL_LASTAWARDS)
if __name__ == '__main__':
main()

View File

@ -39,6 +39,9 @@ subscriber_map = redis_server_map.pubsub(ignore_subscribe_messages=True)
subscriber_map.psubscribe(cfg.get('RedisMap', 'channelDisp'))
subscriber_lastContrib = redis_server_log.pubsub(ignore_subscribe_messages=True)
subscriber_lastContrib.psubscribe(cfg.get('RedisLog', 'channelLastContributor'))
subscriber_lastAwards = redis_server_log.pubsub(ignore_subscribe_messages=True)
subscriber_lastAwards.psubscribe(cfg.get('RedisLog', 'channelLastAwards'))
eventNumber = 0
##########
@ -149,7 +152,7 @@ def geo():
@app.route("/contrib")
def contrib():
categ_list = contributor_helper.categories_in_datatable
categ_list_str = [ s[0].upper() + s[1:].replace('_', ' ') for s in contributor_helper.categories_in_datatable]
categ_list_str = [ s[0].upper() + s[1:].replace('_', ' ') for s in categ_list]
categ_list_points = [contributor_helper.DICO_PNTS_REWARD[categ] for categ in categ_list]
org_rank = contributor_helper.org_rank
@ -163,6 +166,15 @@ def contrib():
org_honor_badge_title_list = [ [num, text] for num, text in contributor_helper.org_honor_badge_title.items()]
org_honor_badge_title_list.sort(key=lambda x: x[0])
trophy_categ_list = contributor_helper.categories_in_trophy
trophy_categ_list_str = [ s[0].upper() + s[1:].replace('_', ' ') for s in trophy_categ_list]
trophy_title = contributor_helper.trophy_title
trophy_title_str = []
for i in range(contributor_helper.trophyNum+1):
trophy_title_str.append(trophy_title[i])
trophy_mapping = ["Top 1"] + [ str(x)+"%" for x in contributor_helper.trophyMapping] + [" "]
trophy_mapping.reverse()
currOrg = request.args.get('org')
if currOrg is None:
currOrg = ""
@ -179,9 +191,20 @@ def contrib():
org_rank_additional_text=org_rank_additional_text,
org_honor_badge_title=json.dumps(org_honor_badge_title),
org_honor_badge_title_list=org_honor_badge_title_list,
trophy_categ_list=json.dumps(trophy_categ_list),
trophy_categ_list_id=trophy_categ_list,
trophy_categ_list_str=trophy_categ_list_str,
trophy_title=json.dumps(trophy_title),
trophy_title_str=trophy_title_str,
trophy_mapping=trophy_mapping,
min_between_reload=cfg.getint('CONTRIB', 'min_between_reload')
)
@app.route("/users")
def users():
return render_template('users.html',
)
''' INDEX '''
@app.route("/_logs")
@ -292,6 +315,10 @@ def getLastContributors():
def getLastContributor():
return Response(eventStreamLastContributor(), mimetype="text/event-stream")
@app.route("/_eventStreamAwards")
def getLastStreamAwards():
return Response(eventStreamAwards(), mimetype="text/event-stream")
def eventStreamLastContributor():
for msg in subscriber_lastContrib.listen():
content = msg['data'].decode('utf8')
@ -303,6 +330,18 @@ def eventStreamLastContributor():
to_return['epoch'] = epoch
yield 'data: {}\n\n'.format(json.dumps(to_return))
def eventStreamAwards():
for msg in subscriber_lastAwards.listen():
content = msg['data'].decode('utf8')
contentJson = json.loads(content)
lastAwardJson = json.loads(contentJson['log'])
org = lastAwardJson['org']
to_return = contributor_helper.getContributorFromRedis(org)
epoch = lastAwardJson['epoch']
to_return['epoch'] = epoch
to_return['award'] = lastAwardJson['award']
yield 'data: {}\n\n'.format(json.dumps(to_return))
@app.route("/_getTopContributor")
def getTopContributor(suppliedDate=None):
if suppliedDate is None:
@ -326,6 +365,15 @@ def getFameContributor():
date = (datetime.datetime(today.year, today.month, 1) - datetime.timedelta(days=1))
return getTopContributor(suppliedDate=date)
@app.route("/_getFameQualContributor")
def getFameQualContributor():
try:
date = datetime.datetime.fromtimestamp(float(request.args.get('date')))
except:
today = datetime.datetime.now()
# get previous month
date = (datetime.datetime(today.year, today.month, 1) - datetime.timedelta(days=1))
return getTopContributor(suppliedDate=date)
@app.route("/_getTop5Overtime")
def getTop5Overtime():
@ -348,6 +396,15 @@ def getCategPerContrib():
return jsonify(contributor_helper.getCategPerContribFromRedis(date))
@app.route("/_getLatestAwards")
def getLatestAwards():
try:
date = datetime.datetime.fromtimestamp(float(request.args.get('date')))
except:
date = datetime.datetime.now()
return jsonify(contributor_helper.getLastAwardsFromRedis())
@app.route("/_getAllOrg")
def getAllOrg():
return jsonify(contributor_helper.getAllOrgFromRedis())
@ -376,5 +433,13 @@ def getHonorBadges():
org = ''
return jsonify(contributor_helper.getOrgHonorBadges(org))
@app.route("/_getTrophies")
def getTrophies():
try:
org = request.args.get('org')
except:
org = ''
return jsonify(contributor_helper.getOrgTrophies(org))
if __name__ == '__main__':
app.run(host='localhost', port=8001, threaded=True)

View File

@ -16,8 +16,24 @@
border: 1px solid #caccce;
}
.circleBadgeSmall {
width: 73px;
height: 73px;
text-align: center;
border-radius: 38px;
background-color: #e1e1e1;
border: 1px solid #caccce;
vertical-align: middle;
margin-left: auto;
margin-right: auto;
}
.circlBadgeNotAcquired { filter: grayscale(100%); }
.circlBadgeAcquired {
box-shadow: 0px 0px 15px #0000ff
/*box-shadow: 0px 0px 15px #0000ff*/
box-shadow: 0px 0px 3px #1b6a92, 0px 0px 10px #2fa1db;
}
.questionBadgeText {
@ -59,7 +75,7 @@
font-size: x-large;
}
.popover-content {
.popoverNoPadding {
padding: 3px
}
@ -79,6 +95,12 @@
min-width: 45px;
}
#panelawards .dataTables_scrollBody {
overflow-x:hidden !important;
overflow-y:auto !important;
}
.dataTables_filter > label {
margin-bottom: 0px;
}
@ -123,6 +145,16 @@
padding-left: 5px;
}
.col-lg-3 {
padding-right: 5px;
padding-left: 5px;
}
.col-lg-9 {
padding-right: 5px;
padding-left: 5px;
}
.col-lg-12 {
padding-right: 5px;
padding-left: 5px;

View File

@ -1,12 +1,14 @@
/* GLOB VAR */
var allOrg = [];
var datatableTop;
var datatableFame;
var datatableFameQuant;
var refresh_speed = min_between_reload*60;
var next_effect = new Date();
var will_reload = $("#reloadCheckbox").is(':checked');
var sec_before_reload = refresh_speed;
var dataTop5Overtime;
var plotLineChart
var plotLineChart;
var source_awards;
var source_lastContrib;
/* CONFIG */
var maxRank = 16;
@ -14,7 +16,8 @@ var popOverOption = {
trigger: "hover",
html: true,
placement: 'bottom',
content: generateRankingSheet()
content: generateRankingSheet(),
template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content popoverNoPadding"></div></div>'
}
var optionsLineChart = {
series: {
@ -65,6 +68,8 @@ var optionDatatable_light = {
};
var optionDatatable_top = jQuery.extend({}, optionDatatable_light)
var optionDatatable_last = jQuery.extend({}, optionDatatable_light)
optionDatatable_last["ordering"] = true;
optionDatatable_last["order"] = [[ 0, "dec" ]];
optionDatatable_last.columnDefs = [
{ className: "small", "targets": [ 0 ] },
{ className: "verticalAlign", "targets": [ 1 ] },
@ -72,12 +77,7 @@ optionDatatable_last.columnDefs = [
{ className: "centerCellPicOrgLogo", "targets": [ 3 ] },
{ className: "centerCellPicOrgLogo verticalAlign", "targets": [ 4 ] },
{ className: "centerCellPicOrgLogo verticalAlign", "targets": [ 5 ] },
{ className: "verticalAlign", "targets": [ 6 ] },
{ 'orderData':[6], 'targets': [0] },
{
'targets': [6],
'searchable': false
},
{ className: "verticalAlign", "targets": [ 6 ] }
]
var optionDatatable_fame = jQuery.extend({}, optionDatatable_light)
optionDatatable_fame.scrollY = '45vh';
@ -86,7 +86,7 @@ var optionDatatable_Categ = {
responsive: true,
searching: true,
"order": [[ 0, "desc" ]],
scrollY: '38vh',
scrollY: '35vh',
"scrollX": true,
scrollCollapse: true,
paging: false,
@ -97,6 +97,15 @@ var optionDatatable_Categ = {
{ className: "centerCellPicOrgLogo", "targets": [ 4 ]}
]
};
var optionDatatable_awards = jQuery.extend({}, optionDatatable_light);
optionDatatable_awards["ordering"] = true;
optionDatatable_awards["order"] = [[ 0, "dec" ]];
optionDatatable_awards["scrollX"] = false;
optionDatatable_awards["scrollY"] = "40vh";
optionDatatable_awards.columnDefs = [
{ className: "centerCellPicOrgLogo verticalAlign", "targets": [ 1 ] },
{ className: "centerCellPicOrgLogo", "targets": [ 3 ] },
];
var typeaheadOption = {
source: function (query, process) {
@ -170,11 +179,25 @@ function createImg(source, size) {
return obj.outerHTML;
}
function createTrophyImg(rank, size, categ) {
var obj = document.createElement('img');
obj.height = size;
obj.width = size;
obj.style.margin = 'auto';
obj.src = url_baseTrophyLogo+rank+'.png';;
obj.title = trophy_title[rank] + " in " + categ;
obj.type = "image/png"
obj.alt = ""
return obj.outerHTML;
}
function createHonorImg(array, size) {
size = 32;
var div = document.createElement('div');
div.style.boxShadow = '0px 0px 5px #00000099';
div.style.backgroundColor = '#e1e1e1';
if (!Array.isArray(array))
array = [array];
for (badgeNum of array) {
var obj = document.createElement('img');
obj.height = size;
@ -313,8 +336,9 @@ function addLastFromJson(datatable, url) {
function addLastContributor(datatable, data, update) {
var date = new Date(data.epoch*1000);
date.toString = function() {return this.toTimeString().slice(0,-15) +' '+ this.toLocaleDateString(); };
var to_add = [
date.toTimeString().slice(0,-15) +' '+ date.toLocaleDateString(),
date,
data.pnts,
getMonthlyRankIcon(data.rank),
getOrgRankIcon(data.orgRank, 60),
@ -324,15 +348,50 @@ function addLastContributor(datatable, data, update) {
];
if (update == undefined || update == false) {
datatable.row.add(to_add);
datatable.draw();
} else if(update == true) {
var row_added = false;
datatable.rows().every( function() {
if(this.data()[3] == data.org) {
if($(this.data()[6])[0].text == data.org) {
var node = $(datatable.row( this ).node());
datatable.row( this ).data( to_add );
if(next_effect <= new Date()) {
node.effect("slide", 500);
next_effect.setSeconds((new Date()).getSeconds() + 5);
}
row_added = true;
}
datatable.draw();
});
if (!row_added) {
var node = $(datatable.row.add(to_add).draw().node());
node.effect("slide", 700);
}
}
}
function addAwards(datatableAwards, json, playAnim) {
if(json.award[0] == 'contribution_status') {
var award = getOrgRankIcon(json.award[1], 60);
} else if (json.award[0] == 'badge') {
var award = createHonorImg(json.award[1], 20);
} else if (json.award[0] == 'trophy') {
var categ = json.award[1][0];
var award = createTrophyImg(json.award[1][1], 40, categ);
}
var date = new Date(json.epoch*1000);
date.toString = function() {return this.toTimeString().slice(0,-15) +' '+ this.toLocaleDateString(); };
var to_add = [
date,
createImg(json.logo_path, 32),
createOrgLink(json.org),
award,
];
var node = $(datatableAwards.row.add(to_add).draw().node());
if(playAnim)
node.effect("slide", 700);
}
function updateProgressBar(org) {
if(currOrg != org)
return;
@ -394,19 +453,23 @@ function updateProgressHeader(org) {
// update color in other dataTables
datatableTop.rows().every( function() {
var row = this.node();
if(this.data()[5] == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); }
var orgRowName = $(this.data()[5])[0].text;
if(orgRowName == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); }
});
datatableFame.rows().every( function() {
datatableFameQuant.rows().every( function() {
var row = this.node();
if(this.data()[5] == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); }
var orgRowName = $(this.data()[5])[0].text;
if(orgRowName == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); }
});
datatableCateg.rows().every( function() {
var row = this.node();
if(this.data()[5] == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); }
var orgRowName = $(this.data()[5])[0].text;
if(orgRowName == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); }
});
datatableLast.rows().every( function() {
var row = this.node();
if(this.data()[6] == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); }
var orgRowName = $(this.data()[6])[0].text;
if(orgRowName == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); }
});
});
@ -446,39 +509,43 @@ function updateProgressHeader(org) {
$.getJSON( url_getHonorBadges+'?org='+org, function( data ) {
for(var i=0; i<numberOfBadges; i++) { // remove
$('#divBadge_'+(i+1)).removeClass('circlBadgeAcquired');
$('#divBadge_'+(i+1)).addClass('circlBadgeNotAcquired');
}
for(var i=0; i<data.length; i++) { // add
$('#divBadge_'+(i+1)).removeClass('circlBadgeNotAcquired');
$('#divBadge_'+(data[i])).addClass('circlBadgeAcquired');
}
});
// set trophies if acquired
$.getJSON( url_getTrophies+'?org='+org, function( data ) {
var source = url_baseTrophyLogo+0+'.png'
for(var i=0; i<trophy_categ_list.length; i++) { // remove
categ = trophy_categ_list[i];
$('#trophy_'+categ).attr('src', source);
$('#trophy_'+categ).attr('title', "");
try { // in case popover not created
var pop = $('#trophy_'+categ).data('bs.popover');
pop.destroy();
} catch(err) {
}
}
setTimeout(function() { // avoid race condition with destroy
for(var i=0; i<data.length; i++) { // add
categ = data[i].categ;
rank = data[i].trophy_true_rank;
trophy_points = data[i].trophy_points
source = url_baseTrophyLogo+rank+'.png'
$('#trophy_'+categ).attr('src', source);
$('#trophy_'+categ).attr('title', trophy_title[rank]);
$('#trophy_'+categ).popover({title: trophy_title[rank], content: 'Level: '+rank+' ('+trophy_points+' points)', trigger: "hover", placement: "bottom"});
}
}, 300);
});
//update overtake points
updateOvertakePnts();
//Add new data to linechart
var flag_already_displayed = false;
for(obj of dataTop5Overtime) { //check if already displayed
if (obj.label == currOrg) {
flag_already_displayed = true;
break;
}
}
if (!flag_already_displayed) {
$.getJSON( url_getOrgOvertime+'?org='+org, function( data ) {
var toPlot = dataTop5Overtime.slice(0); //cloning data
// transform secs into date
var new_data = [];
for(list of data['data']) {
new_data.push([list[0]*1000, list[1]]);
}
data['data'] = new_data;
toPlot.push(data);
plotLineChart.setData(toPlot);
plotLineChart.setupGrid();
plotLineChart.draw();
});
}
}
function showOnlyOrg() {
@ -497,7 +564,6 @@ function updateTimer() {
if ($("#reloadCheckbox").is(':checked')) {
sec_before_reload--;
if (sec_before_reload < 1) {
source_lastContrib.close();
location.reload();
} else {
$('#labelRemainingTime').text(timeToString(sec_before_reload));
@ -517,14 +583,23 @@ $(document).ready(function() {
updateTimer();
$('#orgName').typeahead(typeaheadOption);
$('#btnCurrRank').popover(popOverOption);
$(window).on("beforeunload", function() {
source_lastContrib.close();
source_awards.close();
});
datatableTop = $('#topContribTable').DataTable(optionDatatable_top);
datatableFame = $('#fameTable').DataTable(optionDatatable_fame);
datatableFameQuant = $('#fameTableQuantity').DataTable(optionDatatable_fame);
datatableFameQual = $('#fameTableQuality').DataTable(optionDatatable_fame);
datatableCateg = $('#categTable').DataTable(optionDatatable_Categ);
datatableLast = $('#lastTable').DataTable(optionDatatable_last);
datatableAwards = $('#awardTable').DataTable(optionDatatable_awards);
// top contributors
addToTableFromJson(datatableTop, url_getTopContributor);
// hall of fame
addToTableFromJson(datatableFame, url_getFameContributor);
addToTableFromJson(datatableFameQuant, url_getFameContributor);
addToTableFromJson(datatableFameQual, url_getFameQualContributor);
// last contributors
addLastFromJson(datatableLast, url_getLastContributor);
// category per contributors
@ -548,30 +623,30 @@ $(document).ready(function() {
}
datatableCateg.draw();
});
// top 5 contrib overtime
$.getJSON( url_getTop5Overtime, function( data ) {
// transform secs into date
for(i in data){
var new_data = [];
for(list of data[i]['data']) {
new_data.push([list[0]*1000, list[1]]);
}
data[i]['data'] = new_data;
// latest awards
$.getJSON( url_getLatestAwards, function( data ) {
for (i in data) {
addAwards(datatableAwards, data[i], false);
}
dataTop5Overtime = data;
plotLineChart = $.plot("#divTop5Overtime", data, optionsLineChart);
});
if(currOrg != "") // currOrg selected
//FIXME: timeout used to wait that all datatables are draw.
setTimeout( function() { updateProgressHeader(currOrg); }, 500);
setTimeout( function() { updateProgressHeader(currOrg); }, 700);
source_lastContrib = new EventSource(url_eventStreamLastContributor);
source_lastContrib.onmessage = function(event) {
var json = jQuery.parseJSON( event.data );
addLastContributor(datatableLast, json, true);
datatableLast.draw();
updateProgressBar(json.org);
updateOvertakePnts();
sec_before_reload = refresh_speed; //reset timer at each contribution
};
source_awards = new EventSource(url_eventStreamAwards);
source_awards.onmessage = function(event) {
var json = jQuery.parseJSON( event.data );
addAwards(datatableAwards, json, true);
updateProgressHeader(currOrg);
};
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -46,8 +46,8 @@
</head>
<body>
<!-- Modal -->
<div id="myModal" class="modal fade" role="dialog">
<!-- Modal Rank -->
<div id="myModalRank" class="modal fade" role="dialog">
<div class="modal-dialog modal-lg" style="width: 1500px;">
<!-- Modal content-->
@ -136,22 +136,6 @@
{% endfor %}
</ul>
</div>
<div class="col-lg-6">
<table>
<tbody>
{% for item in org_honor_badge_title_list %}
<tr style="height: 85px">
<td>
<div id="divBadge_{{ loop.index }}" class="circleBadge">
<img height='64px' width='64px' style="margin-top: 5px;" src="{{ url_for('static', filename='pics/MISPHonorableIcons/1.svg')[:-5]}}{{ item[0] }}.svg" type='image/svg' style="margin: auto;"</img>
</div>
</td>
<td style="padding-left: 15px;">{{ item[1] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
@ -159,10 +143,101 @@
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Modal trophy -->
<div id="myModalTrophy" class="modal fade" role="dialog">
<div class="modal-dialog modal-lg" style="width: 1500px;">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" style="float: left;">Trophies and badges</h3>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<h4>Badges:</h4>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Badge</th>
<th>Requirement</th>
</tr>
</thead>
<tbody id='bodyTableBadgeModal'>
{% for item in org_honor_badge_title_list %}
<tr>
<td>
<div id="divBadge_{{ loop.index }}" class="circleBadgeSmall circlBadgeNotAcquired">
<img height='64px' width='64px' class="" style="margin-top: 3px;" src="{{ url_for('static', filename='pics/MISPHonorableIcons/1.svg')[:-5]}}{{ item[0] }}.svg" type='image/svg' style="margin: auto;"</img>
</div>
</td>
<td style="padding-left: 15px;">{{ item[1] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<p style="font-size: 18px; display: inline;">Trophies: </p><p style="display: inline;">Shows your skills in information sharing </p><i> (earned via upvotes or sightings from other organisation)</i>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
{% for title in trophy_title_str %}
<th>{{ title }}</th>
{% endfor %}
</tr>
</thead>
<tbody id='bodyTableTrophyModal'>
<tr>
{% for perc in trophy_mapping %}
<td>{{ perc }}</td>
{% endfor %}
</tr>
<tr>
{% for title in trophy_title_str %}
<td>
<input type='image' style="display: block; margin-left: auto; margin-right: auto;" height="64" width="64" src="{{ url_for('static', filename='pics/MISPTrophy/'+loop.index0|string+'.png') }}">
</td>
{% endfor %}
</tr>
</tbody>
</table>
</div>
<p style="font-size: 18px; display: inline;">Acquired trophies: </p>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
{% for categ in trophy_categ_list_str %}
<th>{{ categ }}</th>
{% endfor %}
</tr>
</thead>
<tbody id='bodyTableTrophyModal'>
<tr>
{% for categ in trophy_categ_list_id %}
<td>
<input type='image' id='trophy_{{categ}}' style="display: block; margin-left: auto; margin-right: auto;" height="64" width="64" src="{{ url_for('static', filename='pics/MISPTrophy/0.png') }}">
</td>
{% endfor %}
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div id="wrapper">
<!-- Navigation -->
@ -190,7 +265,8 @@
<div class='textTopHeader' style="padding-top: 9px;">
<input type="text" id="orgName" data-provide="typeahead" size="20" style="margin-bottom: 5px;">
</div>
<button type="button" class="questionBadgeDiv fa fa-question-circle" data-toggle="modal" data-target="#myModal"></button>
<button type="button" class="questionBadgeDiv fa fa-question-circle" title="Ranking information" data-toggle="modal" data-target="#myModalRank"></button>
<button type="button" class="questionBadgeDiv fa fa-trophy" title="Trophies and badges" data-toggle="modal" data-target="#myModalTrophy"></button>
<button id="btnCurrRank" class='btn btn-default popOverBtn' data-container='body' data-toggle='popover' style="display: none; margin-left: 20px;" onclick="showOnlyOrg()">
<object id='orgContributionRank' height=32 width=64 class="centerInBtn"></object>
<strong id="orgText" class="centerInBtn"></strong>
@ -204,7 +280,7 @@
<div id="orgNextRankDiv" class='textTopHeader' style="padding-top: 0px; position: relative; width: 40px; height: 40px;"></div>
</button>
<div class='leftSepa textTopHeader'>
<span class="label label-primary">
<span class="label label-primary" style="padding: .5em;">
<strong style="">Points to overtake</strong>
<strong style="font-size: medium;" id='orgToOverTake'></strong>
<strong style="">: </strong>
@ -222,14 +298,14 @@
<div class="row">
<div class="col-lg-12">
<div style="height: 10px;"></div>
<div class="col-lg-8">
<div class="col-lg-9">
<div class="col-lg-6">
<div class="panel panel-default" style="height: 100%;">
<div class="panel-heading bg-info" style="font-weight: bold;">
<i class="fa fa-asterisk " style="margin-right: 5px; color: #f0ad4e;"></i><b>Contributor ranking (monthly)</b>
</div>
<div id="panelRanking" class="panel-body" style="height: 100%;">
<div class="panel-body" style="height: 100%;">
<div class="table-responsive">
<table id="topContribTable" class="table table-hover table-striped">
@ -283,11 +359,12 @@
</div>
<div class="col-lg-12">
<div class="col-lg-6">
<div class="panel panel-default" style="height: 100%;">
<div class="panel-heading bg-info" style="font-weight: bold;">
<i class="fa fa-th-list " style="margin-right: 5px; color: #f0ad4e;"></i><b>Contributors and categories (total)</b>
</div>
<div id="panelRanking" class="panel-body" style="height: 100%;">
<div class="panel-body" style="height: 100%;">
<div class="table-responsive">
<table id="categTable" class="table table-hover table-striped">
@ -314,20 +391,47 @@
</div><!-- /.panel -->
</div>
<div class="col-lg-6">
<div class="panel panel-default" style="height: 100%;">
<div class="panel-heading bg-info" style="font-weight: bold;">
<i class="fa fa-trophy" style="margin-right: 5px; color: #d9534f;"></i><b>Latest awards</b>
</div>
<div id="panelawards" class="panel-body" style="height: 100%;">
<div class="table-responsive">
<table id="awardTable" class="table table-hover table-striped">
<thead>
<tr>
<th>Date</th>
<th></th>
<th>Org.</th>
<th>Awards</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<!-- /.panel-body -->
</div><!-- /.panel -->
</div>
</div>
</div><!-- /.col-lg-8 -->
<div class="col-lg-4">
<div class="col-lg-3">
<div class="panel panel-default" style="height: 100%;">
<div class="panel-heading bg-info" style="font-weight: bold;">
<i class="fa fa-star" style="margin-right: 5px; color: #f0ad4e;"></i><b>Hall Of Fame (previous month)</b>
</div>
<div id="panelRanking" class="panel-body" style="height: 100%;">
<div class="panel-body" style="height: 100%;">
<div class="table-responsive">
<table id="fameTable" class="table table-hover table-striped" style="margin-bottom: 0px;">
<table id="fameTableQuantity" class="table table-hover table-striped" style="margin-bottom: 0px;">
<thead>
<tr>
<th>Points</th>
@ -338,7 +442,7 @@
<th>Organisation</th>
</tr>
</thead>
<tbody id=fameTableBody>
<tbody id=fameTableQuantityBody>
</tbody>
</table>
</div>
@ -349,13 +453,29 @@
<div class="panel panel-default" style="height: 100%;">
<div class="panel-heading bg-info" style="font-weight: bold;">
<i class="fa fa-line-chart" style="margin-right: 5px; color: #f0ad4e;"></i><b>Top 5 Contributor overtime</b>
<i class="fa fa-star" style="margin-right: 5px; color: #f0ad4e;"></i><b>Hall Of Fame - Quality (previous month)</b>
</div>
<div id="panelRanking" class="panel-body" style="height: 100%;">
<div id="divTop5Overtime" style="height: 32vh"></div>
<div class="panel-body" style="height: 100%;">
<div class="table-responsive">
<table id="fameTableQuality" class="table table-hover table-striped" style="margin-bottom: 0px;">
<thead>
<tr>
<th>Points</th>
<th>Prev. rank</th>
<th>Org. rank</th>
<th></th>
<th></th>
<th>Organisation</th>
</tr>
</thead>
<tbody id=fameTableQualityBody>
</tbody>
</table>
</div>
</div>
<!-- /.panel-body -->
</div><!-- /.panel -->
</div><!-- /.col-lg-4 -->
@ -375,21 +495,28 @@
/* URL */
var url_getTopContributor = "{{ url_for('getTopContributor') }}";
var url_getFameContributor = "{{ url_for('getFameContributor') }}";
var url_getFameQualContributor = "{{ url_for('getFameQualContributor') }}";
var url_getCategPerContrib = "{{ url_for('getCategPerContrib') }}";
var url_getTop5Overtime = "{{ url_for('getTop5Overtime') }}";
var url_getOrgOvertime = "{{ url_for('getOrgOvertime') }}";
var url_getLastContributor = "{{ url_for('getLastContributors') }}";
var url_getLatestAwards = "{{ url_for('getLatestAwards') }}";
var url_eventStreamLastContributor = "{{ url_for('getLastContributor') }}";
var url_eventStreamAwards = "{{ url_for('getLastStreamAwards') }}";
var url_getAllOrg = "{{ url_for('getAllOrg') }}";
var url_getOrgRank = "{{ url_for('getOrgRank') }}";
var url_getContributionOrgStatus = "{{ url_for('getContributionOrgStatus') }}";
var url_getHonorBadges = "{{ url_for('getHonorBadges') }}";
var url_getTrophies = "{{ url_for('getTrophies')}}"
var url_baseRankMonthlyLogo = "{{ url_for('static', filename='pics/rankingMISPMonthly/1.svg') }}";
url_baseRankMonthlyLogo = url_baseRankMonthlyLogo.substring(0, url_baseRankMonthlyLogo.length-5);
var url_baseOrgRankLogo = "{{ url_for('static', filename='pics/rankingMISPOrg/1.svg') }}";
url_baseOrgRankLogo = url_baseOrgRankLogo.substring(0, url_baseOrgRankLogo.length-5);
var url_baseHonorLogo = "{{ url_for('static', filename='pics/MISPHonorableIcons/1.svg') }}";
url_baseHonorLogo = url_baseHonorLogo.substring(0, url_baseHonorLogo.length-5);
var url_baseTrophyLogo = "{{ url_for('static', filename='pics/MISPTrophy/1.png') }}";
url_baseTrophyLogo = url_baseTrophyLogo.substring(0, url_baseTrophyLogo.length-5);
/* DATA FROM CONF */
var currOrg = "{{ currOrg }}";
@ -399,6 +526,8 @@
var org_rank_obj = JSON.parse('{{ org_rank_json|safe }}');
var org_honor_badge_title = JSON.parse('{{ org_honor_badge_title|safe }}');
var numberOfBadges = {{ org_honor_badge_title_list|length }};
var trophy_categ_list = JSON.parse('{{ trophy_categ_list|safe }}');
var trophy_title = JSON.parse('{{ trophy_title|safe }}');
</script>
<script src="{{ url_for('static', filename='js/contrib.js') }}"></script>

View File

@ -25,6 +25,7 @@ ONE_DAY = 60*60*24
ZMQ_URL = cfg.get('RedisGlobal', 'zmq_url')
CHANNEL = cfg.get('RedisLog', 'channel')
CHANNEL_LASTCONTRIB = cfg.get('RedisLog', 'channelLastContributor')
CHANNEL_LASTAWARDS = cfg.get('RedisLog', 'channelLastAwards')
CHANNELDISP = cfg.get('RedisMap', 'channelDisp')
CHANNEL_PROC = cfg.get('RedisMap', 'channelProc')
PATH_TO_DB = cfg.get('RedisMap', 'pathMaxMindDB')
@ -151,14 +152,22 @@ def handleContribution(zmq_name, org, contribType, categ, action, pntMultiplier=
#CONTRIB_CATEG retain the contribution per category, not the point earned in this categ
push_to_redis_zset('CONTRIB_CATEG', org, count=1, endSubkey=':'+noSpaceLower(categ))
publish_log(zmq_name, 'CONTRIBUTION', {'org': org, 'categ': categ, 'action': action, 'epoch': nowSec }, channel=CHANNEL_LASTCONTRIB)
else:
categ = ""
serv_redis_db.sadd('CONTRIB_ALL_ORG', org)
serv_redis_db.zadd('CONTRIB_LAST:'+util.getDateStrFormat(now), nowSec, org)
serv_redis_db.expire('CONTRIB_LAST:'+util.getDateStrFormat(now), ONE_DAY) #expire after 1 day
serv_redis_db.expire('CONTRIB_LAST:'+util.getDateStrFormat(now), ONE_DAY*7) #expire after 7 day
contributor_helper.updateOrgContributionRank(org, pnts_to_add, action, contribType, eventTime=datetime.datetime.now(), isLabeled=isLabeled)
awards_given = contributor_helper.updateOrgContributionRank(org, pnts_to_add, action, contribType, eventTime=datetime.datetime.now(), isLabeled=isLabeled, categ=noSpaceLower(categ))
for award in awards_given:
# update awards given
serv_redis_db.zadd('CONTRIB_LAST_AWARDS:'+util.getDateStrFormat(now), nowSec, json.dumps({'org': org, 'award': award, 'epoch': nowSec }))
serv_redis_db.expire('CONTRIB_LAST_AWARDS:'+util.getDateStrFormat(now), ONE_DAY*7) #expire after 7 day
# publish
publish_log(zmq_name, 'CONTRIBUTION', {'org': org, 'award': award, 'epoch': nowSec }, channel=CHANNEL_LASTAWARDS)
##############