Improve trophy ranking algorithm, improved ui, added trophy leaderboard per category

pull/43/merge
Sami Mokaddem 2018-09-25 14:48:13 +02:00
parent f22a9db067
commit dc8a944b23
6 changed files with 125 additions and 47 deletions

View File

@ -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

View File

@ -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}'

View File

@ -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 '''

View File

@ -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;

View File

@ -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);
});
});

View File

@ -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,32 +181,35 @@
</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 class="centerCell">{{ title }}</th>
{% endfor %}
</tr>
</thead>
<tbody id='bodyTableTrophyModal'>
<tr>
{% for perc in trophy_mapping %}
<td class="centerCell">{{ 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;">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>
{% for title in trophy_title_str %}
<th class="centerCell">{{ title }}</th>
{% endfor %}
</tr>
</thead>
<tbody id='bodyTableTrophyModal'>
<tr>
{% for perc in trophy_mapping %}
<td class="centerCell">{{ 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>
</div>
<p style="font-size: 18px; display: inline;">Acquired trophies: </p>
@ -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);