mirror of https://github.com/MISP/misp-dashboard
Improve trophy ranking algorithm, improved ui, added trophy leaderboard per category
parent
f22a9db067
commit
dc8a944b23
|
@ -80,7 +80,7 @@ regularlyDays=7
|
|||
|
||||
[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]
|
||||
trophyMapping=[2, 9, 9, 10, 10, 16, 16, 10, 10, 4, 4]
|
||||
|
||||
[HonorTrophy]
|
||||
0=No trophy
|
||||
|
|
|
@ -48,6 +48,7 @@ class Contributor_helper:
|
|||
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.trophyMappingIncremental = [sum(self.trophyMapping[:i]) for i in range(len(self.trophyMapping)+1)]
|
||||
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 = {}
|
||||
|
@ -352,7 +353,6 @@ class Contributor_helper:
|
|||
|
||||
''' TROPHIES '''
|
||||
def getOrgTrophies(self, org):
|
||||
self.getAllOrgsTrophyRanking()
|
||||
keyname = '{mainKey}:{orgCateg}'
|
||||
trophy = []
|
||||
for categ in self.categories_in_trophy:
|
||||
|
@ -360,12 +360,13 @@ class Contributor_helper:
|
|||
totNum = self.serv_redis_db.zcard(key)
|
||||
if totNum == 0:
|
||||
continue
|
||||
pos = self.serv_redis_db.zrank(key, org)
|
||||
pos = self.serv_redis_db.zrevrank(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]})
|
||||
trophy.append({ 'categ': categ, 'trophy_points': trophy_Pnts, 'trophy_rank': trophy_rank, 'trophy_true_rank': self.trophyNum-trophy_rank, 'trophy_title': self.trophy_title[trophy_rank]})
|
||||
return trophy
|
||||
|
||||
def getOrgsTrophyRanking(self, categ):
|
||||
|
@ -374,27 +375,29 @@ class Contributor_helper:
|
|||
res = [[org.decode('utf8'), score] for org, score in res]
|
||||
return res
|
||||
|
||||
def getAllOrgsTrophyRanking(self):
|
||||
def getAllOrgsTrophyRanking(self, category=None):
|
||||
concerned_categ = self.categories_in_trophy if category is None else category
|
||||
dico_categ = {}
|
||||
for categ in self.categories_in_trophy:
|
||||
for categ in [concerned_categ]:
|
||||
res = self.getOrgsTrophyRanking(categ)
|
||||
# add ranking info
|
||||
tot = len(res)
|
||||
for pos in range(tot):
|
||||
res[pos].append(self.trophyNum-self.posToRankMapping(pos, tot))
|
||||
dico_categ[categ] = res
|
||||
toret = dico_categ if category is None else dico_categ.get(category, [])
|
||||
return toret
|
||||
|
||||
def posToRankMapping(self, pos, totNum):
|
||||
mapping = self.trophyMapping
|
||||
mapping_num = [math.ceil(float(float(totNum*i)/float(100))) for i in mapping]
|
||||
if pos == 0: #first
|
||||
position = 1
|
||||
ratio = pos/totNum*100
|
||||
rank = 0
|
||||
if pos == totNum:
|
||||
return 0
|
||||
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
|
||||
for i in range(len(self.trophyMappingIncremental)):
|
||||
if self.trophyMappingIncremental[i] < ratio <= self.trophyMappingIncremental[i+1]:
|
||||
rank = i+1
|
||||
return rank
|
||||
|
||||
def giveTrophyPointsToOrg(self, org, categ, points):
|
||||
keyname = '{mainKey}:{orgCateg}'
|
||||
|
|
|
@ -429,6 +429,11 @@ def getTrophies():
|
|||
org = ''
|
||||
return jsonify(contributor_helper.getOrgTrophies(org))
|
||||
|
||||
@app.route("/_getAllOrgsTrophyRanking")
|
||||
@app.route("/_getAllOrgsTrophyRanking/<string:categ>")
|
||||
def getAllOrgsTrophyRanking(categ=None):
|
||||
return jsonify(contributor_helper.getAllOrgsTrophyRanking(categ))
|
||||
|
||||
|
||||
''' USERS '''
|
||||
|
||||
|
|
|
@ -3,6 +3,13 @@
|
|||
height: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.modal-xl {
|
||||
width: 90%;
|
||||
max-width:1500px;
|
||||
}
|
||||
}
|
||||
|
||||
.successCell {
|
||||
background-color: #dff0d8 !important
|
||||
}
|
||||
|
@ -191,6 +198,19 @@
|
|||
margin-right:auto;
|
||||
}
|
||||
|
||||
.allOrgRankingDiv {
|
||||
overflow: scroll;
|
||||
max-height: 600px;
|
||||
background-color: #fff;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid rgba(0,0,0,.2);
|
||||
border-radius: 6px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
|
|
|
@ -551,6 +551,27 @@ function updateProgressHeader(org) {
|
|||
updateOvertakePnts();
|
||||
}
|
||||
|
||||
function generate_table_ranking_on_category(categ) {
|
||||
$.getJSON( url_getAllOrgsTrophyRanking+'/'+categ, function( data ) {
|
||||
var body = $('#bodyTableThropyAllOrgRankingModal');
|
||||
body.empty();
|
||||
data.forEach(function(arr, i) {
|
||||
var org = arr[0];
|
||||
var points = arr[1];
|
||||
var rank = arr[2];
|
||||
var tr = $('<tr></tr>');
|
||||
tr.append($('<td style="width: 100px;">'+i+'</td>'));
|
||||
tr.append($('<td style="width: 100px;"><img src="'+url_baseTrophyLogo+rank+'.png" width="30" height="30"></td>'));
|
||||
tr.append($('<td style="width: 200px;">'+points+'</td>'));
|
||||
tr.append($('<td><a href="?org='+org+'">'+org+'</a></td>'));
|
||||
if (currOrg == org) {
|
||||
tr.addClass('selectedOrgInTable');
|
||||
}
|
||||
body.append(tr);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function update_timeout_last_added_contrib() {
|
||||
clearTimeout(timeout_last_added_contrib);
|
||||
timeout_last_added_contrib = setTimeout(function() {
|
||||
|
@ -661,4 +682,13 @@ $(document).ready(function() {
|
|||
addAwards(datatableAwards, json, true);
|
||||
updateProgressHeader(currOrg);
|
||||
};
|
||||
|
||||
|
||||
$('#bodyTableTrophyModalOrg input').off('click').on('click', function(e) {
|
||||
var categ = $(this).data('category');
|
||||
var tds = $('#bodyTableTrophyModalOrg td');
|
||||
tds.removeClass('success');
|
||||
$(this).parent().addClass('success');
|
||||
generate_table_ranking_on_category(categ);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
|
||||
<!-- Modal trophy -->
|
||||
<div id="myModalTrophy" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-lg" style="width: 1500px;">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
|
@ -181,9 +181,11 @@
|
|||
</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">
|
||||
<div>
|
||||
<p style="font-size: 18px; display: inline;">Trophies: </p><a style="display: inline;" class="collapsed" data-toggle="collapse" href="#collapsibleTrophyInfo" aria-expanded="false">Shows your skills in information sharing <span class="fa fa-caret-down"></span></a><i> (earned via upvotes or sightings from other organisation)</i>
|
||||
|
||||
<div id="collapsibleTrophyInfo" class="table-responsive collapse">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -208,6 +210,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 18px; display: inline;">Acquired trophies: </p>
|
||||
<div class="table-responsive">
|
||||
|
@ -219,11 +222,11 @@
|
|||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id='bodyTableTrophyModal'>
|
||||
<tbody id='bodyTableTrophyModalOrg'>
|
||||
<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') }}">
|
||||
<input type='image' id='trophy_{{categ}}' data-category='{{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>
|
||||
|
@ -231,6 +234,22 @@
|
|||
</table>
|
||||
</div>
|
||||
|
||||
<div class='allOrgRankingDiv'>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rank</th>
|
||||
<th>Trophy</th>
|
||||
<th>Points</th>
|
||||
<th>Org</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id='bodyTableThropyAllOrgRankingModal'>
|
||||
<tr><td>Click on a category to view the global ranking</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -510,6 +529,7 @@
|
|||
var url_getContributionOrgStatus = "{{ url_for('getContributionOrgStatus') }}";
|
||||
var url_getHonorBadges = "{{ url_for('getHonorBadges') }}";
|
||||
var url_getTrophies = "{{ url_for('getTrophies')}}"
|
||||
var url_getAllOrgsTrophyRanking = "{{ url_for('getAllOrgsTrophyRanking')}}"
|
||||
|
||||
var url_baseRankMonthlyLogo = "{{ url_for('static', filename='pics/rankingMISPMonthly/1.svg') }}";
|
||||
url_baseRankMonthlyLogo = url_baseRankMonthlyLogo.substring(0, url_baseRankMonthlyLogo.length-5);
|
||||
|
|
Loading…
Reference in New Issue