From fb9a35960ed0b9e676972214cd5f05c030ab8309 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 30 Oct 2017 16:28:32 +0100 Subject: [PATCH 01/65] Added contrib webpage --- server.py | 45 +++++- templates/contrib.html | 328 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 templates/contrib.html diff --git a/server.py b/server.py index b0b3de7..012f302 100755 --- a/server.py +++ b/server.py @@ -95,7 +95,7 @@ def getZrange(keyCateg, date, topNum): date_str = str(date.year)+str(date.month)+str(date.day) keyname = "{}:{}".format(keyCateg, date_str) data = serv_redis_db.zrange(keyname, 0, 5, desc=True, withscores=True) - data = [ [record[0].decode('utf8'), record[1]] for record in data ] + data = [ [record[0].decode('utf8'), record[1]] for record in data ] return data @@ -108,7 +108,7 @@ def index(): "{:.0f}".format(cfg.getint('Dashboard' ,'size_world_pannel_perc')/100*ratioCorrection), "{:.0f}".format((100-cfg.getint('Dashboard' ,'size_world_pannel_perc'))/100*ratioCorrection) ] - return render_template('index.html', + return render_template('index.html', pannelSize=pannelSize, size_dashboard_width=[cfg.getint('Dashboard' ,'size_dashboard_left_width'), 12-cfg.getint('Dashboard', 'size_dashboard_left_width')], itemToPlot=cfg.get('Dashboard', 'item_to_plot'), @@ -128,6 +128,47 @@ def geo(): default_updateFrequency=cfg.getint('GEO' ,'updateFrequency') ) +@app.route("/contrib") +def contrib(): + return render_template('contrib.html', + ) + +@app.route("/_getTopContributor") +def getTopContributor(): + data = [ + { + 'progression': '', + 'logo_path': 'logo1', + 'org': 'CIRCL', + }, + { + 'progression': '', + 'logo_path': 'logo2', + 'org': 'CASES', + }, + { + 'progression': '', + 'logo_path': 'logo3', + 'org': 'SMILE', + }, + { + 'progression': '', + 'logo_path': 'logo4', + 'org': 'ORG4', + }, + { + 'progression': '', + 'logo_path': 'logo5', + 'org': 'ORG5', + }, + ] + return jsonify(data) + +@app.route("/_getTop5Overtime") +def getTop5Overtime(): + data = [{'label': 'CIRCL', 'data': [[0, 4], [1, 7], [2,14]]}, {'label': 'CASES', 'data': [[0, 1], [1, 5], [2,2]]}] + return jsonify(data) + @app.route("/_getTopCoord") def getTopCoord(): try: diff --git a/templates/contrib.html b/templates/contrib.html new file mode 100644 index 0000000..b7ff665 --- /dev/null +++ b/templates/contrib.html @@ -0,0 +1,328 @@ + + + + + + + + + + + MISP live dashboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+
+
+
+
+ +
+
+ Contributor Ranking +
+
+ +
+ + + + + + + + + + + +
LogoOrganization name
+
+ +
+ +
+ + +
+ + +
+ +
+
+ Hall Of Fame +
+
+ +
+ + + + + + + + + + +
LogoOrganization name
+
+ +
+ +
+ +
+
+ Top 5 Contributor overtime +
+
+
+
+ + +
+ +
+ +
+
+ +
+ +
+ + +
+ + + + + + + From 36f1cd727f35e85357f32ddd2152732bbb881c4e Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 31 Oct 2017 12:35:05 +0100 Subject: [PATCH 02/65] Continuation of contrib webpage UI --- server.py | 9 ++ static/css/ranking.css | 74 +++++++++++++ templates/contrib.html | 230 ++++++++++++++++++++++++++++++++--------- 3 files changed, 264 insertions(+), 49 deletions(-) create mode 100644 static/css/ranking.css diff --git a/server.py b/server.py index 012f302..03fc056 100755 --- a/server.py +++ b/server.py @@ -169,6 +169,15 @@ def getTop5Overtime(): data = [{'label': 'CIRCL', 'data': [[0, 4], [1, 7], [2,14]]}, {'label': 'CASES', 'data': [[0, 1], [1, 5], [2,2]]}] return jsonify(data) +@app.route("/_getCategPerContrib") +def getCategPerContrib(): + data = [["", "", "CIRCL", 723, 21, 32], +["", "", "CASES", 32, 435, 2], +["", "", "SMILE", 65, 231, 3], +["", "", "ORG4", 34, 21, 7], +["", "", "ORG5", 45, 211, 9]] + return jsonify(data) + @app.route("/_getTopCoord") def getTopCoord(): try: diff --git a/static/css/ranking.css b/static/css/ranking.css new file mode 100644 index 0000000..031f46e --- /dev/null +++ b/static/css/ranking.css @@ -0,0 +1,74 @@ + +.ranking { + position: relative; + height: auto; +} + +.table > tbody > tr > td.centerCell { + text-align: center; + min-width: 45px; +} + + +.table > tbody > tr > td.centerCellPic { + padding: 5px; +} + +.star1-1{ + +} + +.star2-1 { + +} +.star2-2 { + left: 0px; +} + +.star3-1 { + top: 6px; + left: 5px; +} +.star3-2 { + top: -8px; + left: 0px; +} +.star3-3 { + top: 6px; + left: -5px; +} + +.star4-1 { + +} +.star4-2 { + left: -6px; +} +.star4-3 { + left: -36px; + top: -10px; +} +.star4-4 { + left: -42px; + top: -10px; +} + +.star5-1 { + top: 7px; +} +.star5-2 { + left: -3px; + top: 7px; +} +.star5-3 { + left: -37px; + top: -7px; +} +.star5-4 { + left: -47px; + top: -0px; +} +.star5-5 { + left: -57px; + top: -7px; +} diff --git a/templates/contrib.html b/templates/contrib.html index b7ff665..7155820 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -65,6 +65,11 @@ padding-left: 5px; } +.col-lg-12 { + padding-right: 5px; + padding-left: 5px; +} + .leftSepa { border-left-color: rgb(221, 221, 221); border-left-style: solid; @@ -119,30 +124,117 @@ small {
-
-
- Contributor Ranking -
-
- -
- - - - - - - - - - - -
LogoOrganization name
+
+
+
+ Contributor Ranking
+
-
- -
+
+ + + + + + + + + + + +
LogoOrganization name
+
+ +
+ +
+
+ +
+
+
+ Top progression +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LogoOrganization name
+
+
+ +
+
+ +
+
+
+ Contributors and Categories +
+
+ +
+ + + + + + + + + + + + + +
LogoOrganization nameNetwork ActivityPayload deliveryOthers
+
+ +
+ +
+
@@ -152,7 +244,7 @@ small {
- Hall Of Fame + Hall Of Fame
@@ -179,7 +271,7 @@ small { Top 5 Contributor overtime
-
+
@@ -240,11 +332,32 @@ small { var datatableTop; var datatableFame; - var optionDatatable = { + + var optionsLineChart = { + series: { + shadowSize: 0 , + lines: { + fill: true, + fillColor: { + colors: [ { opacity: 1 }, { opacity: 0.1 } ] + } + } + }, + points: { show: true }, + lines: { show: true, fill: true }, + grid: { + tickColor: "#dddddd", + borderWidth: 0 + }, + legend: { + show: true, + position: "nw" + } + }; + var optionDatatable_light = { responsive: true, searching: false, ordering: false, - order: [[0, "asc"]], scrollY: '50vh', scrollCollapse: true, paging: false, @@ -259,10 +372,50 @@ small { { className: "centerCell", "targets": [ 0 ] } ] }; + var optionDatatable_light2 = { + responsive: true, + searching: false, + ordering: false, + scrollY: '50vh', + scrollCollapse: true, + paging: false, + "language": { + "lengthMenu": "", + "info": "", + "infoFiltered": "", + "infoEmpty": "", + }, + "info": false, + "columnDefs": [ + { className: "centerCellPic", "targets": [ 0 ] } + ] + }; + + var optionDatatable_Categ = { + ajax: { + url: "{{ url_for('getCategPerContrib') }}", + dataSrc: '' + }, + responsive: true, + searching: false, + scrollY: '50vh', + scrollCollapse: true, + paging: false, + "info": false, + "columnDefs": [ + { className: "centerCell", "targets": [ 0 ] } + ] + }; + + $.getJSON( "{{ url_for('getCategPerContrib') }}", function( data ) { + console.log(data); + }); $(document).ready(function() { - datatableTop = $('#topContribTable').DataTable(optionDatatable); - datatableFame = $('#fameTable').DataTable(optionDatatable); + datatableTop = $('#topContribTable').DataTable(optionDatatable_light); + datatableFame = $('#fameTable').DataTable(optionDatatable_light); + datatableCateg = $('#categTable').DataTable(optionDatatable_Categ); + datatableProgress = $('#topProgressTable').DataTable(optionDatatable_light2); // top contributors $.getJSON( "{{ url_for('getTopContributor') }}", function( data ) { for (i in data) { @@ -300,27 +453,6 @@ small { }); }); - var optionsLineChart = { - series: { - shadowSize: 0 , - lines: { - fill: true, - fillColor: { - colors: [ { opacity: 1 }, { opacity: 0.1 } ] - } - } - }, - points: { show: true }, - lines: { show: true, fill: true }, - grid: { - tickColor: "#dddddd", - borderWidth: 0 - }, - legend: { - show: true, - position: "nw" - } - }; From cd276488040e4e36143db1c301e3e810bede390c Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 31 Oct 2017 12:35:39 +0100 Subject: [PATCH 03/65] Added contrib webpage --- templates/contrib.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/contrib.html b/templates/contrib.html index 7155820..b2de773 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -170,31 +170,31 @@ small { - + - + - + - + - + From 7438af529217e7221af09948f20b8abd7e74e6da Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 31 Oct 2017 15:08:44 +0100 Subject: [PATCH 04/65] Continuation of contrib page (2) --- server.py | 96 +++++++++++++++++++++---- static/css/ranking.css | 2 + templates/contrib.html | 155 +++++++++++++++++++---------------------- 3 files changed, 156 insertions(+), 97 deletions(-) diff --git a/server.py b/server.py index 03fc056..72d57a7 100755 --- a/server.py +++ b/server.py @@ -133,36 +133,67 @@ def contrib(): return render_template('contrib.html', ) -@app.route("/_getTopContributor") -def getTopContributor(): +@app.route("/_getLastContributor") +def getLastContributor(): data = [ { - 'progression': '', + 'rank': random.randint(1,16), 'logo_path': 'logo1', 'org': 'CIRCL', }, { - 'progression': '', + 'rank': random.randint(1,16), 'logo_path': 'logo2', 'org': 'CASES', }, { - 'progression': '', + 'rank': random.randint(1,16), 'logo_path': 'logo3', 'org': 'SMILE', }, { - 'progression': '', + 'rank': random.randint(1,16), 'logo_path': 'logo4', 'org': 'ORG4', }, { - 'progression': '', + 'rank': random.randint(1,16), 'logo_path': 'logo5', 'org': 'ORG5', }, ] - return jsonify(data) + return jsonify(data*2) + +@app.route("/_getTopContributor") +def getTopContributor(): + data = [ + { + 'rank': random.randint(1,16), + 'logo_path': 'logo1', + 'org': 'CIRCL', + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo2', + 'org': 'CASES', + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo3', + 'org': 'SMILE', + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo4', + 'org': 'ORG4', + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo5', + 'org': 'ORG5', + }, + ] + return jsonify(data*2) @app.route("/_getTop5Overtime") def getTop5Overtime(): @@ -171,12 +202,49 @@ def getTop5Overtime(): @app.route("/_getCategPerContrib") def getCategPerContrib(): - data = [["", "", "CIRCL", 723, 21, 32], -["", "", "CASES", 32, 435, 2], -["", "", "SMILE", 65, 231, 3], -["", "", "ORG4", 34, 21, 7], -["", "", "ORG5", 45, 211, 9]] - return jsonify(data) + data = [ + { + 'rank': random.randint(1,16), + 'logo_path': 'logo1', + 'org': 'CIRCL', + 'network_activity': random.randint(100,1600), + 'payload_delivery': random.randint(100,1600), + 'others': random.randint(1,16) + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo2', + 'org': 'CASES', + 'network_activity': random.randint(10,1600), + 'payload_delivery': random.randint(10,1600), + 'others': random.randint(1,16) + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo3', + 'org': 'SMILE', + 'network_activity': random.randint(1,160), + 'payload_delivery': random.randint(1,160), + 'others': random.randint(1,160) + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo4', + 'org': 'ORG4', + 'network_activity': random.randint(1,160), + 'payload_delivery': random.randint(1,160), + 'others': random.randint(1,16) + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo5', + 'org': 'ORG5', + 'network_activity': random.randint(1,16), + 'payload_delivery': random.randint(1,16), + 'others': random.randint(1,16) + }, + ] + return jsonify(data*2) @app.route("/_getTopCoord") def getTopCoord(): diff --git a/static/css/ranking.css b/static/css/ranking.css index 031f46e..ff8cca4 100644 --- a/static/css/ranking.css +++ b/static/css/ranking.css @@ -12,6 +12,8 @@ .table > tbody > tr > td.centerCellPic { padding: 5px; + text-align: center; + min-width: 45px; } .star1-1{ diff --git a/templates/contrib.html b/templates/contrib.html index b2de773..64acf1a 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -135,8 +135,8 @@ small { - - + + @@ -154,51 +154,21 @@ small {
- Top progression + Last contributors
-
+
-
#Org rank Logo Organization name
+
- - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#Org rank LogoOrganization nameOrg name
@@ -218,7 +188,8 @@ small { - + + @@ -252,7 +223,8 @@ small {
#Org rank Logo Organization name Network Activity
- + + @@ -271,7 +243,7 @@ small { Top 5 Contributor overtime
-
+
@@ -295,6 +267,17 @@ small { /* DATA FROM CONF */ + function getRankIcon(rank) { + var imgsPath = "{{ url_for('static', filename='pics/rankingMISP/1.png') }}"; + imgsPath = imgsPath.substring(0, imgsPath.length-5); + rankLogoPath = imgsPath+rank+'.png'; + var img = document.createElement('img'); + img.src = rankLogoPath; + img.height = 26; + img.width = 26; + return img.outerHTML; + } + function create_stars_dom(n) { var n = 4-n; //1 is best -> 3 stars var div = document.createElement('DIV'); @@ -358,7 +341,7 @@ small { responsive: true, searching: false, ordering: false, - scrollY: '50vh', + scrollY: '30vh', scrollCollapse: true, paging: false, "language": { @@ -368,61 +351,34 @@ small { "infoEmpty": "", }, "info": false, - "columnDefs": [ - { className: "centerCell", "targets": [ 0 ] } - ] - }; - var optionDatatable_light2 = { - responsive: true, - searching: false, - ordering: false, - scrollY: '50vh', - scrollCollapse: true, - paging: false, - "language": { - "lengthMenu": "", - "info": "", - "infoFiltered": "", - "infoEmpty": "", - }, - "info": false, - "columnDefs": [ - { className: "centerCellPic", "targets": [ 0 ] } - ] }; + var optionDatatable_top = jQuery.extend({}, optionDatatable_light) + var optionDatatable_last = jQuery.extend({}, optionDatatable_light) + var optionDatatable_fame = jQuery.extend({}, optionDatatable_light) + optionDatatable_fame.scrollY = '50vh'; var optionDatatable_Categ = { - ajax: { - url: "{{ url_for('getCategPerContrib') }}", - dataSrc: '' - }, responsive: true, - searching: false, - scrollY: '50vh', + searching: true, + scrollY: '39vh', scrollCollapse: true, paging: false, "info": false, - "columnDefs": [ - { className: "centerCell", "targets": [ 0 ] } - ] }; - $.getJSON( "{{ url_for('getCategPerContrib') }}", function( data ) { - console.log(data); - }); - $(document).ready(function() { - datatableTop = $('#topContribTable').DataTable(optionDatatable_light); - datatableFame = $('#fameTable').DataTable(optionDatatable_light); + datatableTop = $('#topContribTable').DataTable(optionDatatable_top); + datatableFame = $('#fameTable').DataTable(optionDatatable_fame); datatableCateg = $('#categTable').DataTable(optionDatatable_Categ); - datatableProgress = $('#topProgressTable').DataTable(optionDatatable_light2); + datatableLast = $('#lastTable').DataTable(optionDatatable_last); // top contributors $.getJSON( "{{ url_for('getTopContributor') }}", function( data ) { for (i in data) { var row = data[i]; + i = parseInt(i); var to_add = [ - getIndexRank(), - row.progression, + i+1, + getRankIcon(row.rank), row.logo_path, row.org ]; @@ -434,11 +390,12 @@ small { // hall of fame $.getJSON( "{{ url_for('getTopContributor') }}", function( data ) { datatableIndex = 1; - var table = document.getElementById('fameTableBody'); for (i in data) { var row = data[i]; + i = parseInt(i); var to_add = [ - getIndexRank(), + i+1, + getRankIcon(row.rank), row.logo_path, row.org ]; @@ -447,6 +404,38 @@ small { datatableFame.draw(); }); + $.getJSON( "{{ url_for('getTopContributor') }}", function( data ) { + for (i in data) { + var row = data[i]; + i = parseInt(i); + var to_add = [ + i+1, + getRankIcon(row.rank), + row.logo_path, + row.org + ]; + datatableLast.row.add(to_add); + } + datatableLast.draw(); + }); + + $.getJSON( "{{ url_for('getCategPerContrib') }}", function( data ) { + for (i in data) { + var row = data[i]; + i = parseInt(i); + var to_add = [ + i+1, + getRankIcon(row.rank), + row.logo_path, + row.org, + row.network_activity, + row.payload_delivery, + row.others + ]; + datatableCateg.row.add(to_add); + } + datatableCateg.draw(); + }); // top 5 contrib overtime $.getJSON( "{{ url_for('getTop5Overtime') }}", function( data ) { var plotLineChart = $.plot("#divTop5Overtime", data, optionsLineChart); From 4293ad52c957bb166f5f8218bc821753301d9c31 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 31 Oct 2017 15:11:59 +0100 Subject: [PATCH 05/65] Minor css change --- static/css/ranking.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static/css/ranking.css b/static/css/ranking.css index ff8cca4..18db351 100644 --- a/static/css/ranking.css +++ b/static/css/ranking.css @@ -9,6 +9,9 @@ min-width: 45px; } +.dataTables_filter > label { + margin-bottom: 0px; +} .table > tbody > tr > td.centerCellPic { padding: 5px; From 9fc82589ab321882ba4f02183c7df354ddd4f39b Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 31 Oct 2017 15:49:04 +0100 Subject: [PATCH 06/65] Added typeahead orgname --- server.py | 5 +++++ templates/contrib.html | 51 +++++++++++++++--------------------------- templates/geo.html | 9 ++++---- 3 files changed, 28 insertions(+), 37 deletions(-) diff --git a/server.py b/server.py index 72d57a7..0b5bb5c 100755 --- a/server.py +++ b/server.py @@ -246,6 +246,11 @@ def getCategPerContrib(): ] return jsonify(data*2) +@app.route("/_getAllOrg") +def getAllOrg(): + data = ['CIRCL', 'CASES', 'SMILE' ,'ORG4' ,'ORG5'] + return jsonify(data) + @app.route("/_getTopCoord") def getTopCoord(): try: diff --git a/templates/contrib.html b/templates/contrib.html index 64acf1a..dfdf332 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -26,6 +26,7 @@ + @@ -112,6 +113,11 @@ small {
  • MISP Geolocalisation
  • MISP Contributors
  • +
    +
    + Your organisation name: +
    +
    @@ -278,39 +284,18 @@ small { return img.outerHTML; } - function create_stars_dom(n) { - var n = 4-n; //1 is best -> 3 stars - var div = document.createElement('DIV'); - div.classList.add('ranking'); - for(var i=1; i<=n; i++) { - var span = document.createElement('span'); - span.classList.add("glyphicon"); - span.classList.add("glyphicon-star"); - span.classList.add("star"+n+"-"+i); - div.appendChild(span); + var allOrg = []; + var typeaheadOption = { + source: function (query, process) { + if (allOrg.length == 0) { // caching + return $.getJSON("{{ url_for('getAllOrg') }}", function (data) { + allOrg = data; + return process(data); + }); + } else { + return process(allOrg); + } } - return div.outerHTML; - } - - var datatableIndex = 1; - function getIndexRank() { - var to_return; - switch (datatableIndex) { - case 1: - to_return = create_stars_dom(1); - break; - case 2: - to_return = create_stars_dom(2); - break; - case 3: - to_return = create_stars_dom(3); - break; - - default: - to_return = datatableIndex; - } - datatableIndex++; - return to_return; } var datatableTop; @@ -367,6 +352,7 @@ small { }; $(document).ready(function() { + $('#orgName').typeahead(typeaheadOption); datatableTop = $('#topContribTable').DataTable(optionDatatable_top); datatableFame = $('#fameTable').DataTable(optionDatatable_fame); datatableCateg = $('#categTable').DataTable(optionDatatable_Categ); @@ -389,7 +375,6 @@ small { // hall of fame $.getJSON( "{{ url_for('getTopContributor') }}", function( data ) { - datatableIndex = 1; for (i in data) { var row = data[i]; i = parseInt(i); diff --git a/templates/geo.html b/templates/geo.html index 50302b2..2dfbfde 100644 --- a/templates/geo.html +++ b/templates/geo.html @@ -58,7 +58,7 @@ } .textTopHeader { - height: 50px; + height: 50px; text-align: center; margin-left: 8px; float: left; @@ -90,6 +90,7 @@ small {
    @@ -184,7 +185,7 @@ small {
    - +
    Hit map @@ -198,7 +199,7 @@ small {
    Geospatial information - Dates: + Dates: @@ -225,7 +226,7 @@ small { - + From cfd1818a33bbb2e9b9f5aab36b12664b49f065e8 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 2 Nov 2017 10:16:34 +0100 Subject: [PATCH 09/65] Added ranking table --- server.py | 2 +- static/css/ranking.css | 13 +++++ static/js/contrib.js | 118 +++++++++++++++++++++++------------------ templates/contrib.html | 18 +++---- 4 files changed, 90 insertions(+), 61 deletions(-) diff --git a/server.py b/server.py index c94ccf9..37d07c4 100755 --- a/server.py +++ b/server.py @@ -249,7 +249,7 @@ def getCategPerContrib(): @app.route("/_getAllOrg") def getAllOrg(): - data = ['CIRCL', 'CASES', 'SMILE' ,'ORG4' ,'ORG5'] + data = ['CIRCL', 'CASES', 'SMILE' ,'ORG4' ,'ORG5', 'SUPER HYPER LONG ORGINZATION NAME'] return jsonify(data) @app.route("/_getTopCoord") diff --git a/static/css/ranking.css b/static/css/ranking.css index d6c1205..f9e85cf 100644 --- a/static/css/ranking.css +++ b/static/css/ranking.css @@ -3,6 +3,19 @@ height: auto; } +.popover-content { + padding: 3px +} + +.popOverBtn { + float: left; + padding: 0px; + padding-right: 8px; + height: 46px; + margin-top: 2px; + margin-bottom: 2px; +} + .table > tbody > tr > td.centerCell { text-align: center; min-width: 45px; diff --git a/static/js/contrib.js b/static/js/contrib.js index 1acccfc..72f7c27 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -1,7 +1,17 @@ -/* CONFIG */ +/* GLOB VAR */ var allOrg = []; var datatableTop; var datatableFame; + +/* CONFIG */ +var maxRank = 16; +var popOverOption = { + trigger: "hover", + title: "Ranking icons", + html: true, + placement: 'bottom', + content: generateRankingSheet() +} var optionsLineChart = { series: { shadowSize: 0 , @@ -65,11 +75,12 @@ var typeaheadOption = { }, updater: function(item) { $('#orgText').text(item); + $('#btnCurrRank').show(); } } /* FUNCTIONS */ -function getRankIcon(rank, size) { +function getRankIcon(rank, size, header) { rankLogoPath = url_baseRankLogo+rank+'.png'; var img = document.createElement('img'); img.src = rankLogoPath; @@ -77,11 +88,53 @@ function getRankIcon(rank, size) { img.height = 26; img.width = 26; } else { - img.height = size; - img.width = size; + if (header) { + img.height = size; + img.width = size; + img.style.position = 'absolute'; + img.style.top = '0'; + img.style.bottom = '0'; + img.style.margin = 'auto'; + img.style.left = '0px'; + } else { + img.height = size; + img.width = size; + } } return img.outerHTML; } +function generateRankingSheet() { + var table = document.createElement('table'); + table.classList.add('table', 'table-striped'); + table.style.marginBottom = '0px'; + //head + var thead = document.createElement('thead'); + var tr = document.createElement('tr'); + var th = document.createElement('th'); + th.innerHTML = "Ranking"; + tr.appendChild(th); + var th = document.createElement('th'); + th.innerHTML = "Requirement"; + tr.appendChild(th); + thead.appendChild(tr); + //body + var tbody = document.createElement('tbody'); + for (var i=1; i<=maxRank; i++) { + var tr = document.createElement('tr'); + var td = document.createElement('td'); + td.innerHTML = getRankIcon(i, 20); + td.style.padding = "2px"; + tr.appendChild(td); + var td = document.createElement('td'); + td.innerHTML = i+" pnts"; + td.style.padding = "2px"; + tr.appendChild(td); + tbody.appendChild(tr); + } + table.appendChild(thead); + table.appendChild(tbody); + return table.outerHTML; +} function addToTableFromJson(datatable, url) { $.getJSON( url, function( data ) { @@ -105,60 +158,23 @@ function addToTableFromJson(datatable, url) { $(document).ready(function() { $('#orgName').typeahead(typeaheadOption); - $('#orgRankDiv').html(getRankIcon(8, 50)); - $('#orgNextRankDiv').html(getRankIcon(9, 50)); + $('#orgRankDiv').html(getRankIcon(8, 40, true)); + $('#orgNextRankDiv').html(getRankIcon(9, 40, true)); $('#orgText').text(currOrg); + if(currOrg != "") // currOrg selected + $('#btnCurrRank').show(); + $('[data-toggle="popover"]').popover(popOverOption); datatableTop = $('#topContribTable').DataTable(optionDatatable_top); datatableFame = $('#fameTable').DataTable(optionDatatable_fame); datatableCateg = $('#categTable').DataTable(optionDatatable_Categ); datatableLast = $('#lastTable').DataTable(optionDatatable_last); // top contributors - $.getJSON(url_getTopContributor , function( data ) { - for (i in data) { - var row = data[i]; - i = parseInt(i); - var to_add = [ - i+1, - getRankIcon(row.rank), - row.logo_path, - row.org - ]; - datatableTop.row.add(to_add); - } - datatableTop.draw(); - }); - + addToTableFromJson(datatableTop, url_getTopContributor); // hall of fame - $.getJSON( url_getTopContributor, function( data ) { - for (i in data) { - var row = data[i]; - i = parseInt(i); - var to_add = [ - i+1, - getRankIcon(row.rank), - row.logo_path, - row.org - ]; - datatableFame.row.add(to_add); - } - datatableFame.draw(); - }); - - $.getJSON( url_getTopContributor, function( data ) { - for (i in data) { - var row = data[i]; - i = parseInt(i); - var to_add = [ - i+1, - getRankIcon(row.rank), - row.logo_path, - row.org - ]; - datatableLast.row.add(to_add); - } - datatableLast.draw(); - }); - + addToTableFromJson(datatableFame, url_getTopContributor); + // last contributors + addToTableFromJson(datatableLast, url_getTopContributor); + // category per contributors $.getJSON( url_getCategPerContrib, function( data ) { for (i in data) { var row = data[i]; diff --git a/templates/contrib.html b/templates/contrib.html index d26bfd2..e37296e 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -67,16 +67,16 @@
    -
    -
    -
    -
    -
    -
    +
    -
    -
    +
    +
    From 8e2b5477f2a217b3ed867d5e3416ac38c533c7b1 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 2 Nov 2017 10:57:16 +0100 Subject: [PATCH 10/65] Dynamic update iof org rank in header --- server.py | 9 +++++++++ static/css/ranking.css | 4 +++- static/js/contrib.js | 34 ++++++++++++++++++++++------------ templates/contrib.html | 9 +++++---- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/server.py b/server.py index 37d07c4..38fad50 100755 --- a/server.py +++ b/server.py @@ -252,6 +252,15 @@ def getAllOrg(): data = ['CIRCL', 'CASES', 'SMILE' ,'ORG4' ,'ORG5', 'SUPER HYPER LONG ORGINZATION NAME'] return jsonify(data) +@app.route("/_getOrgRank") +def getOrgRank(): + try: + org = request.args.get('org') + except: + org = '' + data = {'org': org, 'rank': random.randint(1,16)} + return jsonify(data) + @app.route("/_getTopCoord") def getTopCoord(): try: diff --git a/static/css/ranking.css b/static/css/ranking.css index f9e85cf..ef1f564 100644 --- a/static/css/ranking.css +++ b/static/css/ranking.css @@ -9,11 +9,13 @@ .popOverBtn { float: left; - padding: 0px; + padding-top: 2px; + padding-bottom: 2px; padding-right: 8px; height: 46px; margin-top: 2px; margin-bottom: 2px; + display: none; } .table > tbody > tr > td.centerCell { diff --git a/static/js/contrib.js b/static/js/contrib.js index 72f7c27..4fd438b 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -73,9 +73,8 @@ var typeaheadOption = { return process(allOrg); } }, - updater: function(item) { - $('#orgText').text(item); - $('#btnCurrRank').show(); + updater: function(org) { + updateProgressHeader(org); } } @@ -103,7 +102,7 @@ function getRankIcon(rank, size, header) { } return img.outerHTML; } -function generateRankingSheet() { +function generateRankingSheet(rank) { var table = document.createElement('table'); table.classList.add('table', 'table-striped'); table.style.marginBottom = '0px'; @@ -111,7 +110,7 @@ function generateRankingSheet() { var thead = document.createElement('thead'); var tr = document.createElement('tr'); var th = document.createElement('th'); - th.innerHTML = "Ranking"; + th.innerHTML = "Rank"; tr.appendChild(th); var th = document.createElement('th'); th.innerHTML = "Requirement"; @@ -128,6 +127,10 @@ function generateRankingSheet() { var td = document.createElement('td'); td.innerHTML = i+" pnts"; td.style.padding = "2px"; + tr.style.textAlign = "center"; + if (i == rank) { // current org rank + tr.classList.add('info') + } tr.appendChild(td); tbody.appendChild(tr); } @@ -154,16 +157,23 @@ function addToTableFromJson(datatable, url) { } - +function updateProgressHeader(org) { + // get Org rank + $.getJSON( url_getOrgRank+'?org='+org, function( data ) { + $('#btnCurrRank').show(); + $('#orgText').text(data.org); + var popoverRank = $('#btnCurrRank').data('bs.popover'); + popoverRank.options.content = generateRankingSheet(data.rank); + $('#orgRankDiv').html(getRankIcon(data.rank, 40, true)); + $('#orgNextRankDiv').html(getRankIcon(data.rank+1, 40, true)); + }); +} $(document).ready(function() { - $('#orgName').typeahead(typeaheadOption); - $('#orgRankDiv').html(getRankIcon(8, 40, true)); - $('#orgNextRankDiv').html(getRankIcon(9, 40, true)); - $('#orgText').text(currOrg); if(currOrg != "") // currOrg selected - $('#btnCurrRank').show(); - $('[data-toggle="popover"]').popover(popOverOption); + updateProgressHeader(org) + $('#orgName').typeahead(typeaheadOption); + $('#btnCurrRank').popover(popOverOption); datatableTop = $('#topContribTable').DataTable(optionDatatable_top); datatableFame = $('#fameTable').DataTable(optionDatatable_fame); datatableCateg = $('#categTable').DataTable(optionDatatable_Categ); diff --git a/templates/contrib.html b/templates/contrib.html index e37296e..4bea1aa 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -68,14 +68,14 @@
    @@ -234,6 +234,7 @@ var url_getCategPerContrib = "{{ url_for('getCategPerContrib') }}"; var url_getTop5Overtime = "{{ url_for('getTop5Overtime') }}"; var url_getAllOrg = "{{ url_for('getAllOrg') }}"; + var url_getOrgRank = "{{ url_for('getOrgRank') }}"; var url_baseRankLogo = "{{ url_for('static', filename='pics/rankingMISP/1.png') }}"; url_baseRankLogo = url_baseRankLogo.substring(0, url_baseRankLogo.length-5); From 2685a08019220cddfed4ea47a20ebcd04e845b57 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 2 Nov 2017 11:36:23 +0100 Subject: [PATCH 11/65] Added support of rank from org points --- server.py | 13 ++++++++++++- static/js/contrib.js | 9 ++++++--- templates/contrib.html | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/server.py b/server.py index 38fad50..5641287 100755 --- a/server.py +++ b/server.py @@ -98,6 +98,16 @@ def getZrange(keyCateg, date, topNum): data = [ [record[0].decode('utf8'), record[1]] for record in data ] return data +# max lvl is 16 +def getRankLevel(points): + return float("{:.2f}".format(math.log(points, 2))) +def getRemainingPoints(points): + prev = 0 + for i in [2**x for x in range(1,17)]: + if prev <= points < i: + return i-points + prev = i + return 0 @app.route("/") def index(): @@ -258,7 +268,8 @@ def getOrgRank(): org = request.args.get('org') except: org = '' - data = {'org': org, 'rank': random.randint(1,16)} + points = random.randint(1,2**16) + data = {'org': org, 'points': points, 'rank': getRankLevel(points), 'remainingPts': getRemainingPoints(points)} return jsonify(data) @app.route("/_getTopCoord") diff --git a/static/js/contrib.js b/static/js/contrib.js index 4fd438b..26287dd 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -160,12 +160,15 @@ function addToTableFromJson(datatable, url) { function updateProgressHeader(org) { // get Org rank $.getJSON( url_getOrgRank+'?org='+org, function( data ) { + console.log(data); + var rank = Math.floor(data.rank); $('#btnCurrRank').show(); $('#orgText').text(data.org); var popoverRank = $('#btnCurrRank').data('bs.popover'); - popoverRank.options.content = generateRankingSheet(data.rank); - $('#orgRankDiv').html(getRankIcon(data.rank, 40, true)); - $('#orgNextRankDiv').html(getRankIcon(data.rank+1, 40, true)); + popoverRank.options.content = generateRankingSheet(rank); + $('#orgRankDiv').html(getRankIcon(rank, 40, true)); + $('#orgNextRankDiv').html(getRankIcon(rank+1, 40, true)); + $('#progressBarDiv').width((data.rank - rank)*150); //150 is empty bar width }); } diff --git a/templates/contrib.html b/templates/contrib.html index 4bea1aa..d1c9387 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -71,7 +71,7 @@
    -
    +
    From eed0528f525df898d5623bd2c5cb7569f524b7f0 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 2 Nov 2017 11:47:52 +0100 Subject: [PATCH 12/65] Put rankLvl multiplier in config + fixed bug log 0 --- server.py | 11 ++++++++--- static/js/contrib.js | 2 +- templates/contrib.html | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/server.py b/server.py index 5641287..d81a645 100755 --- a/server.py +++ b/server.py @@ -100,10 +100,13 @@ def getZrange(keyCateg, date, topNum): # max lvl is 16 def getRankLevel(points): - return float("{:.2f}".format(math.log(points, 2))) + if points == 0: + return 0 + else: + return float("{:.2f}".format(math.log(points, cfg.getint('CONTRIB' ,'rankMultiplier')))) def getRemainingPoints(points): prev = 0 - for i in [2**x for x in range(1,17)]: + for i in [cfg.getint('CONTRIB' ,'rankMultiplier')**x for x in range(1,17)]: if prev <= points < i: return i-points prev = i @@ -141,7 +144,8 @@ def geo(): @app.route("/contrib") def contrib(): return render_template('contrib.html', - currOrg="" + currOrg="", + rankMultiplier=cfg.getint('CONTRIB' ,'rankMultiplier') ) @app.route("/_getLastContributor") @@ -269,6 +273,7 @@ def getOrgRank(): except: org = '' points = random.randint(1,2**16) + #FIXME put 0 if org has no points data = {'org': org, 'points': points, 'rank': getRankLevel(points), 'remainingPts': getRemainingPoints(points)} return jsonify(data) diff --git a/static/js/contrib.js b/static/js/contrib.js index 26287dd..7a7a412 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -125,7 +125,7 @@ function generateRankingSheet(rank) { td.style.padding = "2px"; tr.appendChild(td); var td = document.createElement('td'); - td.innerHTML = i+" pnts"; + td.innerHTML = Math.pow(rankMultiplier, i)+" pnts"; td.style.padding = "2px"; tr.style.textAlign = "center"; if (i == rank) { // current org rank diff --git a/templates/contrib.html b/templates/contrib.html index d1c9387..56913e1 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -240,6 +240,7 @@ /* DATA FROM CONF */ var currOrg = "{{ currOrg }}"; + var rankMultiplier = {{ rankMultiplier }}; From aabd4779f9147ced54c359ea1ce5cc736f76f5e9 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 2 Nov 2017 13:13:13 +0100 Subject: [PATCH 13/65] Added row coloring for selected ORG --- static/js/contrib.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/static/js/contrib.js b/static/js/contrib.js index 7a7a412..5d1e930 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -160,7 +160,7 @@ function addToTableFromJson(datatable, url) { function updateProgressHeader(org) { // get Org rank $.getJSON( url_getOrgRank+'?org='+org, function( data ) { - console.log(data); + datatableTop.draw(); var rank = Math.floor(data.rank); $('#btnCurrRank').show(); $('#orgText').text(data.org); @@ -169,6 +169,23 @@ function updateProgressHeader(org) { $('#orgRankDiv').html(getRankIcon(rank, 40, true)); $('#orgNextRankDiv').html(getRankIcon(rank+1, 40, true)); $('#progressBarDiv').width((data.rank - rank)*150); //150 is empty bar width + // update color in other dataTables + datatableTop.rows().every( function() { + var row = this.node(); + if(this.data()[3] == data.org) { row.classList.add('info'); } else { row.classList.remove('info'); } + }); + datatableFame.rows().every( function() { + var row = this.node(); + if(this.data()[3] == data.org) { row.classList.add('info'); } else { row.classList.remove('info'); } + }); + datatableCateg.rows().every( function() { + var row = this.node(); + if(this.data()[3] == data.org) { row.classList.add('info'); } else { row.classList.remove('info'); } + }); + datatableLast.rows().every( function() { + var row = this.node(); + if(this.data()[3] == data.org) { row.classList.add('info'); } else { row.classList.remove('info'); } + }); }); } From 3bfa5185dc01e7d96cad7df8f5f30a501ac5736c Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 2 Nov 2017 14:02:54 +0100 Subject: [PATCH 14/65] Few additional ui adjustement + filtering categ feature on button click --- static/css/ranking.css | 9 +++++++++ static/js/contrib.js | 11 +++++++---- templates/contrib.html | 6 ++---- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/static/css/ranking.css b/static/css/ranking.css index ef1f564..e63626d 100644 --- a/static/css/ranking.css +++ b/static/css/ranking.css @@ -3,6 +3,15 @@ height: auto; } +.centerInBtn { + float: left; + text-align: center; + vertical-align: middle; + line-height: 40px; + margin-right: 10px; + font-size: x-large; +} + .popover-content { padding: 3px } diff --git a/static/js/contrib.js b/static/js/contrib.js index 5d1e930..2abe1ff 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -7,7 +7,6 @@ var datatableFame; var maxRank = 16; var popOverOption = { trigger: "hover", - title: "Ranking icons", html: true, placement: 'bottom', content: generateRankingSheet() @@ -113,7 +112,7 @@ function generateRankingSheet(rank) { th.innerHTML = "Rank"; tr.appendChild(th); var th = document.createElement('th'); - th.innerHTML = "Requirement"; + th.innerHTML = "Requirement (CP)"; tr.appendChild(th); thead.appendChild(tr); //body @@ -125,7 +124,7 @@ function generateRankingSheet(rank) { td.style.padding = "2px"; tr.appendChild(td); var td = document.createElement('td'); - td.innerHTML = Math.pow(rankMultiplier, i)+" pnts"; + td.innerHTML = Math.pow(rankMultiplier, i); td.style.padding = "2px"; tr.style.textAlign = "center"; if (i == rank) { // current org rank @@ -156,10 +155,10 @@ function addToTableFromJson(datatable, url) { }); } - function updateProgressHeader(org) { // get Org rank $.getJSON( url_getOrgRank+'?org='+org, function( data ) { + console.log(data); datatableTop.draw(); var rank = Math.floor(data.rank); $('#btnCurrRank').show(); @@ -189,6 +188,10 @@ function updateProgressHeader(org) { }); } +function showOnlyOrg() { + datatableCateg.search( $('#orgText').text() ).draw(); +} + $(document).ready(function() { if(currOrg != "") // currOrg selected updateProgressHeader(org) diff --git a/templates/contrib.html b/templates/contrib.html index 56913e1..1a13c0c 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -64,10 +64,8 @@
    -
    - -
    -
    - - - - + {% for categ in categ_list_str %} + + {% endfor %} @@ -239,6 +238,8 @@ /* DATA FROM CONF */ var currOrg = "{{ currOrg }}"; var rankMultiplier = {{ rankMultiplier }}; + var categ_list = JSON.parse('{{ categ_list|safe }}'); + var categ_list_str = JSON.parse("{{ categ_list_str|safe }}"); From be20f75ad83e26bd089b2b2cc2ff1896e7d390d5 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 2 Nov 2017 16:21:04 +0100 Subject: [PATCH 20/65] Other UI adjustement --- server.py | 2 +- static/js/contrib.js | 2 +- templates/contrib.html | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/server.py b/server.py index 68efcfb..28c880d 100755 --- a/server.py +++ b/server.py @@ -238,7 +238,7 @@ def getCategPerContrib(): @app.route("/_getAllOrg") def getAllOrg(): - data = ['CIRCL', 'CASES', 'SMILE' ,'ORG4' ,'ORG5', 'SUPER HYPER LONG ORGINZATION NAME'] + data = ['CIRCL', 'CASES', 'SMILE' ,'ORG4' ,'ORG5', 'SUPER HYPER LONG ORGINZATION NAME', 'Org3'] return jsonify(data) @app.route("/_getOrgRank") diff --git a/static/js/contrib.js b/static/js/contrib.js index 26aa59e..a17e541 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -55,7 +55,7 @@ optionDatatable_fame.scrollY = '50vh'; var optionDatatable_Categ = { responsive: true, searching: true, - scrollY: '39vh', + scrollY: '38vh', "scrollX": true, scrollCollapse: true, paging: false, diff --git a/templates/contrib.html b/templates/contrib.html index 0b09812..940d471 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -100,9 +100,9 @@ - + - + @@ -127,9 +127,9 @@ - + - + @@ -153,10 +153,11 @@ - + + {% for categ in categ_list_str %} - + {% endfor %} @@ -187,9 +188,9 @@ - + - + @@ -239,7 +240,6 @@ var currOrg = "{{ currOrg }}"; var rankMultiplier = {{ rankMultiplier }}; var categ_list = JSON.parse('{{ categ_list|safe }}'); - var categ_list_str = JSON.parse("{{ categ_list_str|safe }}"); From 76d93ecda81aa9d4d33b55e2365cd87f98c27f7a Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Fri, 3 Nov 2017 08:55:03 +0100 Subject: [PATCH 21/65] Improved ranking sheet with overall progression --- static/js/contrib.js | 37 ++++++++++++++++++++++++++++++------- templates/contrib.html | 2 +- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/static/js/contrib.js b/static/js/contrib.js index a17e541..123766e 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -106,10 +106,13 @@ function getRankIcon(rank, size, header) { } return img.outerHTML; } -function generateRankingSheet(rank, stepPnt, pnt, Rpnt) { +function generateRankingSheet(rank, rankDec, stepPnt, pnt, Rpnt) { var Cpnt = pnt - stepPnt; var Tpnt = Cpnt + Rpnt; + var OuterDiv = document.createElement('div'); var gdiv = document.createElement('div'); + gdiv.id = "globalDiv"; + gdiv.style.float = 'left'; //progressBar var div = document.createElement('div'); div.classList.add('progress'); @@ -119,19 +122,18 @@ function generateRankingSheet(rank, stepPnt, pnt, Rpnt) { var div1 = document.createElement('div') div1.classList.add('progress-bar') div1.style.width = 100*(Cpnt)/Tpnt+'%'; - div1.innerHTML = pnt; + div1.innerHTML = ""+Cpnt+""; div.appendChild(div1); var div1 = document.createElement('div') div1.classList.add('progress-bar', 'progress-bar-warning') div1.style.width = 100*(Rpnt)/Tpnt+'%' - div1.innerHTML = Rpnt; + div1.innerHTML = ""+Rpnt+""; div.appendChild(div1); gdiv.appendChild(div); - // table var table = document.createElement('table'); table.classList.add('table', 'table-striped'); - table.style.marginBottom = '0px'; + table.style.marginBottom = '5px'; //head var thead = document.createElement('thead'); var tr = document.createElement('tr'); @@ -167,7 +169,26 @@ function generateRankingSheet(rank, stepPnt, pnt, Rpnt) { table.appendChild(thead); table.appendChild(tbody); gdiv.appendChild(table); - return gdiv.outerHTML; + OuterDiv.appendChild(gdiv); + // Tot nbr points + var tableHeight = 440; //HARDCODED... + var div = document.createElement('div'); + div.classList.add('progress'); + div.style.width = '20px'; + div.style.height = tableHeight+'px'; //HARDCODED... + div.style.display = 'flex' + div.style.float = 'left'; + div.style.marginTop = '56px'; + div.style.marginBottom = '0px'; + div.style.marginLeft = '2px'; + var div1 = document.createElement('div') + div1.classList.add('progress-bar', 'progress-bar-success', 'progress-bar-striped') + div1.style.height = ((rank+rankDec)*100/16)+'%'; + div1.style.width = '100%'; + div.appendChild(div1); + OuterDiv.appendChild(div); + + return OuterDiv.outerHTML; } function addToTableFromJson(datatable, url) { @@ -190,12 +211,14 @@ function addToTableFromJson(datatable, url) { function updateProgressHeader(org) { // get Org rank $.getJSON( url_getOrgRank+'?org='+org, function( data ) { + console.log(data); datatableTop.draw(); var rank = Math.floor(data.rank); + var rankDec = data.rank-rank; $('#btnCurrRank').show(); $('#orgText').text(data.org); var popoverRank = $('#btnCurrRank').data('bs.popover'); - popoverRank.options.content = generateRankingSheet(rank, data.stepPts, data.points, data.remainingPts); + popoverRank.options.content = generateRankingSheet(rank, rankDec, data.stepPts, data.points, data.remainingPts); $('#orgRankDiv').html(getRankIcon(rank, 40, true)); $('#orgNextRankDiv').html(getRankIcon(rank+1, 40, true)); if (data.rank > 16){ diff --git a/templates/contrib.html b/templates/contrib.html index 940d471..1dcb8e6 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -118,7 +118,7 @@
    - Last contributors + Last contributors (24h)
    From 0e5b03fee6ee058e9a30e43e70d95c00556a68fd Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Fri, 3 Nov 2017 09:32:07 +0100 Subject: [PATCH 22/65] Draft of adding contribution info to redis --- config/config.cfg.default | 8 +++++-- server.py | 15 ++++++++----- zmq_subscriber.py | 47 +++++++++++++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/config/config.cfg.default b/config/config.cfg.default index 988cc7b..4a568fb 100644 --- a/config/config.cfg.default +++ b/config/config.cfg.default @@ -20,9 +20,13 @@ zoomlevel = 11 clusteringDistance = 10 [CONTRIB] -#[1.5 -> +inf] +max_number_of_last_contributor = 10 +#How much harder it gets to rank up (exponential multiplier) [1.5 -> +inf] rankMultiplier = 2 -categories = ["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" ] +categories_in_datatable = ["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" ] +default_pnts_per_contribution = 1 +# array of the form [[category, pntsRcv], ...] +pnts_per_contribution = [["payload_delivery", 5], ["artifact_dropped", 20], ["network_activity", 5]] [Log] field_to_plot = Attribute.category diff --git a/server.py b/server.py index 28c880d..842871a 100755 --- a/server.py +++ b/server.py @@ -28,7 +28,7 @@ serv_redis_db = redis.StrictRedis( port=cfg.getint('RedisGlobal', 'port'), db=cfg.getint('RedisDB', 'db')) -categories = json.loads(cfg.get('CONTRIB', 'categories')) +categories_in_datatable = json.loads(cfg.get('CONTRIB', 'categories_in_datatable')) subscriber_log = redis_server_log.pubsub(ignore_subscribe_messages=True) subscriber_log.psubscribe(cfg.get('RedisLog', 'channel')) @@ -145,8 +145,8 @@ def geo(): @app.route("/contrib") def contrib(): - categ_list = json.loads(cfg.get('CONTRIB', 'categories')) - categ_list_str = [ s[0].upper() + s[1:].replace('_', ' ') for s in json.loads(cfg.get('CONTRIB', 'categories'))] + categ_list = categories_in_datatable + categ_list_str = [ s[0].upper() + s[1:].replace('_', ' ') for s in categories_in_datatable] return render_template('contrib.html', currOrg="", rankMultiplier=cfg.getfloat('CONTRIB' ,'rankMultiplier'), @@ -230,7 +230,7 @@ def getCategPerContrib(): dic['rank'] = random.randint(1,16) dic['logo_path'] = 'logo' dic['org'] = 'Org'+str(d) - for f in categories: + for f in categories_in_datatable: dic[f] = random.randint(0,1600) data.append(dic) @@ -250,7 +250,12 @@ def getOrgRank(): points = random.randint(1,math.floor(cfg.getfloat('CONTRIB' ,'rankMultiplier')**16)) #FIXME put 0 if org has no points remainingPts = getRemainingPoints(points) - data = {'org': org, 'points': points, 'rank': getRankLevel(points), 'remainingPts': remainingPts['remainingPts'], 'stepPts': remainingPts['stepPts']} + data = {'org': org, + 'points': points, + 'rank': getRankLevel(points), + 'remainingPts': remainingPts['remainingPts'], + 'stepPts': remainingPts['stepPts'], + } return jsonify(data) @app.route("/_getTopCoord") diff --git a/zmq_subscriber.py b/zmq_subscriber.py index e0f6b99..cd01536 100755 --- a/zmq_subscriber.py +++ b/zmq_subscriber.py @@ -24,6 +24,14 @@ CHANNELDISP = cfg.get('RedisMap', 'channelDisp') CHANNEL_PROC = cfg.get('RedisMap', 'channelProc') PATH_TO_DB = cfg.get('RedisMap', 'pathMaxMindDB') +DEFAULT_PNTS_REWARD = cfg.get('CONTRIB', 'default_pnts_per_contribution') +categories_in_datatable = json.loads(cfg.get('CONTRIB', 'categories_in_datatable')) +DICO_PNTS_REWARD = {} +temp = json.loads(cfg.get('CONTRIB', 'pnts_per_contribution')) +for categ, pnts in temp: + DICO_PNTS_REWARD[categ] = pnts +MAX_NUMBER_OF_LAST_CONTRIBUTOR = cfg.getint('CONTRIB', 'max_number_of_last_contributor') + serv_log = redis.StrictRedis( host=cfg.get('RedisGlobal', 'host'), port=cfg.getint('RedisGlobal', 'port'), @@ -44,19 +52,18 @@ def publish_log(zmq_name, name, content): to_send = { 'name': name, 'log': json.dumps(content), 'zmqName': zmq_name } serv_log.publish(CHANNEL, json.dumps(to_send)) -def push_to_redis_zset(keyCateg, toAdd): +def push_to_redis_zset(keyCateg, toAdd, endSubkey="", count=1): now = datetime.datetime.now() - today_str = str(now.year)+str(now.month)+str(now.day) - keyname = "{}:{}".format(keyCateg, today_str) - serv_redis_db.zincrby(keyname, toAdd) + today_str = str(now.year)+str(now.month).zfill(2)+str(now.day).zfill(2) + keyname = "{}:{}{}".format(keyCateg, today_str, endSubkey) + serv_redis_db.zincrby(keyname, toAdd, count) def push_to_redis_geo(keyCateg, lon, lat, content): now = datetime.datetime.now() - today_str = str(now.year)+str(now.month)+str(now.day) + today_str = str(now.year)+str(now.month).zfill(2)+str(now.day).zfill(2) keyname = "{}:{}".format(keyCateg, today_str) serv_redis_db.geoadd(keyname, lon, lat, content) - def ip_to_coord(ip): resp = reader.city(ip) lat = float(resp.location.latitude) @@ -109,6 +116,28 @@ def getFields(obj, fields): except KeyError as e: return "" +def noSpaceLower(str): + return str.lower().replace(' ', '_') + +#pntMultiplier if one contribution rewards more than others. (e.g. shighting may gives more points than editing) +def handleContribution(org, categ, action, pntMultiplier=1): + if action in ['edit']: + pass + #return #not a contribution? + # is a valid contribution + try: + pnts_to_add = DICO_PNTS_REWARD[noSpaceLower(categ)] + except KeyError: + pnts_to_add = DEFAULT_PNTS_REWARD + pnts_to_add *= pntMultiplier + + push_to_redis_zset('CONTRIB_DAY', org, count=pnts_to_add) + #CONTRIB_CATEG retain the contribution per category, not the point earned in this categ + push_to_redis_zset('CONTRIB_CATEG', org, count=DEFAULT_PNTS_REWARD, endSubkey=':'+noSpaceLower(categ)) + serv_redis_db.sadd('CONTRIB_ALL_ORG', org) + serv_redis_db.lpush('CONTRIB_LAST', org) + serv_redis_db.ltrim('CONTRIB_LAST', 0, MAX_NUMBER_OF_LAST_CONTRIBUTOR-1) # Limit list size + #serv_redis_db.lrange('CONTRIB_LAST', 0, MAX_NUMBER_OF_LAST_CONTRIBUTOR-1) # get the last 10 contributors ############## ## HANDLERS ## @@ -129,6 +158,10 @@ def handler_keepalive(zmq_name, jsonevent): def handler_sighting(zmq_name, jsonsight): print('sending' ,'sighting') + org = jsonsight['org'] + categ = jsonsight['categ'] + action = jsonsight['action'] + handleContribution(org, categ, action) return def handler_event(zmq_name, jsonobj): @@ -165,6 +198,7 @@ def handler_attribute(zmq_name, jsonobj): if jsonattr['category'] == "Network activity": getCoordAndPublish(zmq_name, jsonattr['value'], jsonattr['category']) + handleContribution(jsonobj['Event']['Orgc']['name'], jsonattr['category'], jsonobj['action']) # Push to log publish_log(zmq_name, 'Attribute', to_push) @@ -210,4 +244,3 @@ if __name__ == "__main__": main(args.zmqname) reader.close() - From 2ca318b68e030abf23064591324f28026632a0b7 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Fri, 3 Nov 2017 09:35:51 +0100 Subject: [PATCH 23/65] Moved function into section for server script --- server.py | 204 +++++++++++++++++++++++++++++------------------------- 1 file changed, 108 insertions(+), 96 deletions(-) diff --git a/server.py b/server.py index 842871a..a771f41 100755 --- a/server.py +++ b/server.py @@ -114,6 +114,12 @@ def getRemainingPoints(points): prev = i return { 'remainingPts': 0, 'stepPts': cfg.getfloat('CONTRIB' ,'rankMultiplier')**16 } +########### +## ROUTE ## +########### + +''' MAIN ROUTE ''' + @app.route("/") def index(): ratioCorrection = 88 @@ -154,6 +160,108 @@ def contrib(): categ_list_str=categ_list_str ) +''' INDEX ''' + +@app.route("/_logs") +def logs(): + return Response(event_stream_log(), mimetype="text/event-stream") + +@app.route("/_maps") +def maps(): + return Response(event_stream_maps(), mimetype="text/event-stream") + +@app.route("/_get_log_head") +def getLogHead(): + return json.dumps(LogItem('').get_head_row()) + +def event_stream_log(): + for msg in subscriber_log.listen(): + content = msg['data'] + yield EventMessage(content).to_json() + +def event_stream_maps(): + for msg in subscriber_map.listen(): + content = msg['data'].decode('utf8') + yield 'data: {}\n\n'.format(content) + +''' GEO ''' + +@app.route("/_getTopCoord") +def getTopCoord(): + try: + date = datetime.datetime.fromtimestamp(float(request.args.get('date'))) + except: + date = datetime.datetime.now() + keyCateg = "GEO_COORD" + topNum = 6 # default Num + data = getZrange(keyCateg, date, topNum) + return jsonify(data) + +@app.route("/_getHitMap") +def getHitMap(): + try: + date = datetime.datetime.fromtimestamp(float(request.args.get('date'))) + except: + date = datetime.datetime.now() + keyCateg = "GEO_COUNTRY" + topNum = -1 # default Num + data = getZrange(keyCateg, date, topNum) + return jsonify(data) + +def isCloseTo(coord1, coord2): + clusterMeter = cfg.getfloat('GEO' ,'clusteringDistance') + clusterThres = math.pow(10, len(str(abs(clusterMeter)))-7) #map meter to coord threshold (~ big approx) + if abs(float(coord1[0]) - float(coord2[0])) <= clusterThres: + if abs(float(coord1[1]) - float(coord2[1])) <= clusterThres: + return True + return False + +@app.route("/_getCoordsByRadius") +def getCoordsByRadius(): + dico_coord = {} + to_return = [] + try: + dateStart = datetime.datetime.fromtimestamp(float(request.args.get('dateStart'))) + dateEnd = datetime.datetime.fromtimestamp(float(request.args.get('dateEnd'))) + centerLat = request.args.get('centerLat') + centerLon = request.args.get('centerLon') + radius = int(math.ceil(float(request.args.get('radius')))) + except: + return jsonify(to_return) + + delta = dateEnd - dateStart + for i in range(delta.days+1): + correctDatetime = dateStart + datetime.timedelta(days=i) + date_str = str(correctDatetime.year)+str(correctDatetime.month)+str(correctDatetime.day) + keyCateg = 'GEO_RAD' + keyname = "{}:{}".format(keyCateg, date_str) + res = serv_redis_db.georadius(keyname, centerLon, centerLat, radius, unit='km', withcoord=True) + + #sum up really close coord + for data, coord in res: + flag_added = False + coord = [coord[0], coord[1]] + #list all coord + for dicoCoordStr in dico_coord.keys(): + dicoCoord = json.loads(dicoCoordStr) + #if curCoord close to coord + if isCloseTo(dicoCoord, coord): + #add data to dico coord + dico_coord[dicoCoordStr].append(data) + flag_added = True + break + # coord not in dic + if not flag_added: + dico_coord[str(coord)] = [data] + + for dicoCoord, array in dico_coord.items(): + dicoCoord = json.loads(dicoCoord) + to_return.append([array, dicoCoord]) + + return jsonify(to_return) + +''' CONTRIB ''' + @app.route("/_getLastContributor") def getLastContributor(): data = [ @@ -258,101 +366,5 @@ def getOrgRank(): } return jsonify(data) -@app.route("/_getTopCoord") -def getTopCoord(): - try: - date = datetime.datetime.fromtimestamp(float(request.args.get('date'))) - except: - date = datetime.datetime.now() - keyCateg = "GEO_COORD" - topNum = 6 # default Num - data = getZrange(keyCateg, date, topNum) - return jsonify(data) - -@app.route("/_getHitMap") -def getHitMap(): - try: - date = datetime.datetime.fromtimestamp(float(request.args.get('date'))) - except: - date = datetime.datetime.now() - keyCateg = "GEO_COUNTRY" - topNum = -1 # default Num - data = getZrange(keyCateg, date, topNum) - return jsonify(data) - -def isCloseTo(coord1, coord2): - clusterMeter = cfg.getfloat('GEO' ,'clusteringDistance') - clusterThres = math.pow(10, len(str(abs(clusterMeter)))-7) #map meter to coord threshold (~ big approx) - if abs(float(coord1[0]) - float(coord2[0])) <= clusterThres: - if abs(float(coord1[1]) - float(coord2[1])) <= clusterThres: - return True - return False - -@app.route("/_getCoordsByRadius") -def getCoordsByRadius(): - dico_coord = {} - to_return = [] - try: - dateStart = datetime.datetime.fromtimestamp(float(request.args.get('dateStart'))) - dateEnd = datetime.datetime.fromtimestamp(float(request.args.get('dateEnd'))) - centerLat = request.args.get('centerLat') - centerLon = request.args.get('centerLon') - radius = int(math.ceil(float(request.args.get('radius')))) - except: - return jsonify(to_return) - - delta = dateEnd - dateStart - for i in range(delta.days+1): - correctDatetime = dateStart + datetime.timedelta(days=i) - date_str = str(correctDatetime.year)+str(correctDatetime.month)+str(correctDatetime.day) - keyCateg = 'GEO_RAD' - keyname = "{}:{}".format(keyCateg, date_str) - res = serv_redis_db.georadius(keyname, centerLon, centerLat, radius, unit='km', withcoord=True) - - #sum up really close coord - for data, coord in res: - flag_added = False - coord = [coord[0], coord[1]] - #list all coord - for dicoCoordStr in dico_coord.keys(): - dicoCoord = json.loads(dicoCoordStr) - #if curCoord close to coord - if isCloseTo(dicoCoord, coord): - #add data to dico coord - dico_coord[dicoCoordStr].append(data) - flag_added = True - break - # coord not in dic - if not flag_added: - dico_coord[str(coord)] = [data] - - for dicoCoord, array in dico_coord.items(): - dicoCoord = json.loads(dicoCoord) - to_return.append([array, dicoCoord]) - - return jsonify(to_return) - -@app.route("/_logs") -def logs(): - return Response(event_stream_log(), mimetype="text/event-stream") - -@app.route("/_maps") -def maps(): - return Response(event_stream_maps(), mimetype="text/event-stream") - -@app.route("/_get_log_head") -def getLogHead(): - return json.dumps(LogItem('').get_head_row()) - -def event_stream_log(): - for msg in subscriber_log.listen(): - content = msg['data'] - yield EventMessage(content).to_json() - -def event_stream_maps(): - for msg in subscriber_map.listen(): - content = msg['data'].decode('utf8') - yield 'data: {}\n\n'.format(content) - if __name__ == '__main__': app.run(host='localhost', port=8001, threaded=True) From ac90101fc2273301dc41201bfb2ae778e7eef16a Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Fri, 3 Nov 2017 09:38:11 +0100 Subject: [PATCH 24/65] Same for util function (move into section) --- server.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/server.py b/server.py index a771f41..194100d 100755 --- a/server.py +++ b/server.py @@ -36,6 +36,11 @@ subscriber_map = redis_server_map.pubsub(ignore_subscribe_messages=True) subscriber_map.psubscribe(cfg.get('RedisMap', 'channelDisp')) eventNumber = 0 +########## +## UTIL ## +########## + +''' INDEX ''' class LogItem(): FIELDNAME_ORDER = [] @@ -93,12 +98,7 @@ class EventMessage(): to_ret = { 'log': self.feed, 'feedName': self.feedName, 'zmqName': self.zmqName } return 'data: {}\n\n'.format(json.dumps(to_ret)) -def getZrange(keyCateg, date, topNum): - date_str = str(date.year)+str(date.month)+str(date.day) - keyname = "{}:{}".format(keyCateg, date_str) - data = serv_redis_db.zrange(keyname, 0, 5, desc=True, withscores=True) - data = [ [record[0].decode('utf8'), record[1]] for record in data ] - return data +''' CONTRIB ''' # max lvl is 16 def getRankLevel(points): @@ -106,6 +106,7 @@ def getRankLevel(points): return 0 else: return float("{:.2f}".format(math.log(points, cfg.getfloat('CONTRIB' ,'rankMultiplier')))) + def getRemainingPoints(points): prev = 0 for i in [math.floor(cfg.getfloat('CONTRIB' ,'rankMultiplier')**x) for x in range(1,17)]: @@ -114,6 +115,16 @@ def getRemainingPoints(points): prev = i return { 'remainingPts': 0, 'stepPts': cfg.getfloat('CONTRIB' ,'rankMultiplier')**16 } + +''' GENERAL ''' + +def getZrange(keyCateg, date, topNum): + date_str = str(date.year)+str(date.month).zfill(2)+str(date.day).zfill(2) + keyname = "{}:{}".format(keyCateg, date_str) + data = serv_redis_db.zrange(keyname, 0, 5, desc=True, withscores=True) + data = [ [record[0].decode('utf8'), record[1]] for record in data ] + return data + ########### ## ROUTE ## ########### From 9f6638bf293c3c7b49cf9e67389345bc928cd805 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Fri, 3 Nov 2017 09:42:05 +0100 Subject: [PATCH 25/65] Fixed small bug related to dateformat --- server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.py b/server.py index 194100d..0a17cf5 100755 --- a/server.py +++ b/server.py @@ -243,7 +243,7 @@ def getCoordsByRadius(): delta = dateEnd - dateStart for i in range(delta.days+1): correctDatetime = dateStart + datetime.timedelta(days=i) - date_str = str(correctDatetime.year)+str(correctDatetime.month)+str(correctDatetime.day) + date_str = str(correctDatetime.year)+str(correctDatetime.month).zfill(2)+str(correctDatetime.day).zfill(2) keyCateg = 'GEO_RAD' keyname = "{}:{}".format(keyCateg, date_str) res = serv_redis_db.georadius(keyname, centerLon, centerLat, radius, unit='km', withcoord=True) From 264a4e52e2d7d334a9c2cab3f4d83201fa95a2fb Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 6 Nov 2017 09:50:28 +0100 Subject: [PATCH 26/65] Draft getting contribution info from redis --- server.py | 228 +++++++++++++++++++++++++++++++++-------- static/js/contrib.js | 8 +- templates/contrib.html | 10 +- zmq_subscriber.py | 14 ++- 4 files changed, 207 insertions(+), 53 deletions(-) diff --git a/server.py b/server.py index 0a17cf5..1903fdf 100755 --- a/server.py +++ b/server.py @@ -29,6 +29,7 @@ serv_redis_db = redis.StrictRedis( db=cfg.getint('RedisDB', 'db')) categories_in_datatable = json.loads(cfg.get('CONTRIB', 'categories_in_datatable')) +MAX_NUMBER_OF_LAST_CONTRIBUTOR = cfg.getint('CONTRIB', 'max_number_of_last_contributor') subscriber_log = redis_server_log.pubsub(ignore_subscribe_messages=True) subscriber_log.psubscribe(cfg.get('RedisLog', 'channel')) @@ -104,9 +105,14 @@ class EventMessage(): def getRankLevel(points): if points == 0: return 0 + elif points == 1: + return 1 else: return float("{:.2f}".format(math.log(points, cfg.getfloat('CONTRIB' ,'rankMultiplier')))) +def getTrueRank(ptns): + return int(getRankLevel(ptns)) + def getRemainingPoints(points): prev = 0 for i in [math.floor(cfg.getfloat('CONTRIB' ,'rankMultiplier')**x) for x in range(1,17)]: @@ -117,14 +123,99 @@ def getRemainingPoints(points): ''' GENERAL ''' +def getMonthSpan(date): + ds = datetime.datetime(date.year, date.month, 1) + dyear = 1 if ds.month+1 > 12 else 0 + dmonth = -12 if ds.month+1 > 12 else 0 + de = datetime.datetime(ds.year + dyear, ds.month+1 + dmonth, 1) -def getZrange(keyCateg, date, topNum): - date_str = str(date.year)+str(date.month).zfill(2)+str(date.day).zfill(2) - keyname = "{}:{}".format(keyCateg, date_str) - data = serv_redis_db.zrange(keyname, 0, 5, desc=True, withscores=True) + delta = de - ds + to_return = [] + for i in range(delta.days): + to_return.append(ds + datetime.timedelta(days=i)) + return to_return + +def getDateStrFormat(date): + return str(date.year)+str(date.month).zfill(2)+str(date.day).zfill(2) + +def getZrange(keyCateg, date, topNum, endSubkey=""): + date_str = getDateStrFormat(date) + keyname = "{}:{}{}".format(keyCateg, date_str, endSubkey) + data = serv_redis_db.zrange(keyname, 0, topNum-1, desc=True, withscores=True) data = [ [record[0].decode('utf8'), record[1]] for record in data ] return data +def getOrgPntFromRedis(org, date): + keyCateg = 'CONTRIB_DAY' + scoreSum = 0 + for curDate in getMonthSpan(date): + date_str = getDateStrFormat(curDate) + keyname = "{}:{}".format(keyCateg, date_str) + data = serv_redis_db.zscore(keyname, org) + if data is None: + data = 0 + scoreSum += data + return scoreSum + +def getOrgRankFromRedis(org, date): + ptns = getOrgPntFromRedis(org, date) + return getTrueRank(ptns) + +def getOrgLogoFromRedis(org): + return 'logo_'+org + +def getTopContributor_fromRedis(date): + data2 = [ + { + 'rank': random.randint(1,16), + 'logo_path': 'logo1', + 'org': 'CIRCL', + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo2', + 'org': 'CASES', + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo3', + 'org': 'SMILE', + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo4', + 'org': 'ORG4', + }, + { + 'rank': random.randint(1,16), + 'logo_path': 'logo5', + 'org': 'ORG5', + }, + ] + + orgDicoPnts = {} + for curDate in getMonthSpan(date): + keyCateg = "CONTRIB_DAY" + topNum = 0 # all + contrib_org = getZrange(keyCateg, curDate, topNum) + for org, pnts in contrib_org: + if org not in orgDicoPnts: + orgDicoPnts[org] = 0 + orgDicoPnts[org] += pnts + + data = [] + for org, pnts in orgDicoPnts.items(): + dic = {} + dic['rank'] = getTrueRank(pnts) + dic['logo_path'] = getOrgLogoFromRedis(org) + dic['org'] = org + dic['pnts'] = pnts + data.append(dic) + data.sort(key=lambda x: x['pnts'], reverse=True) + + return data + #return data2 + ########### ## ROUTE ## ########### @@ -215,7 +306,7 @@ def getHitMap(): except: date = datetime.datetime.now() keyCateg = "GEO_COUNTRY" - topNum = -1 # default Num + topNum = 0 # all data = getZrange(keyCateg, date, topNum) return jsonify(data) @@ -243,7 +334,7 @@ def getCoordsByRadius(): delta = dateEnd - dateStart for i in range(delta.days+1): correctDatetime = dateStart + datetime.timedelta(days=i) - date_str = str(correctDatetime.year)+str(correctDatetime.month).zfill(2)+str(correctDatetime.day).zfill(2) + date_str = getDateStrFormat(correctDatetime) keyCateg = 'GEO_RAD' keyname = "{}:{}".format(keyCateg, date_str) res = serv_redis_db.georadius(keyname, centerLon, centerLat, radius, unit='km', withcoord=True) @@ -275,7 +366,7 @@ def getCoordsByRadius(): @app.route("/_getLastContributor") def getLastContributor(): - data = [ + data2 = [ { 'rank': random.randint(1,16), 'logo_path': 'logo1', @@ -302,48 +393,77 @@ def getLastContributor(): 'org': 'ORG5', }, ] - return jsonify(data*2) + try: + date = datetime.datetime.fromtimestamp(float(request.args.get('date'))) + except: + date = datetime.datetime.now() + keyCateg = "CONTRIB_LAST" + topNum = MAX_NUMBER_OF_LAST_CONTRIBUTOR # default Num + last_contrib_org = getZrange(keyCateg, date, topNum) + data = [] + for org, sec in last_contrib_org: + dic = {} + dic['rank'] = getOrgRankFromRedis(org, datetime.datetime.now()) + dic['logo_path'] = getOrgLogoFromRedis(org) + dic['org'] = org + dic['pnts'] = getOrgPntFromRedis(org, date) + data.append(dic) + return jsonify(data) + #return jsonify(data2*2) @app.route("/_getTopContributor") -def getTopContributor(): - data = [ - { - 'rank': random.randint(1,16), - 'logo_path': 'logo1', - 'org': 'CIRCL', - }, - { - 'rank': random.randint(1,16), - 'logo_path': 'logo2', - 'org': 'CASES', - }, - { - 'rank': random.randint(1,16), - 'logo_path': 'logo3', - 'org': 'SMILE', - }, - { - 'rank': random.randint(1,16), - 'logo_path': 'logo4', - 'org': 'ORG4', - }, - { - 'rank': random.randint(1,16), - 'logo_path': 'logo5', - 'org': 'ORG5', - }, - ] - return jsonify(data*2) +def getTopContributor(suppliedDate=None): + if suppliedDate is None: + try: + date = datetime.datetime.fromtimestamp(float(request.args.get('date'))) + except: + date = datetime.datetime.now() + else: + date = suppliedDate + + data = getTopContributor_fromRedis(date) + return jsonify(data) + +@app.route("/_getFameContributor") +def getFameContributor(): + 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(date) + @app.route("/_getTop5Overtime") def getTop5Overtime(): - data = [{'label': 'CIRCL', 'data': [[0, 4], [1, 7], [2,14]]}, {'label': 'CASES', 'data': [[0, 1], [1, 5], [2,2]]}] + data2 = [ + {'label': 'CIRCL', 'data': [[0, 4], [1, 7], [2,14]]}, + {'label': 'CASES', 'data': [[0, 1], [1, 5], [2,2]]} + ] + data = [] + today = datetime.datetime.now() + topSortedOrg = getTopContributor_fromRedis(today) #Get current top + # show current top 5 org points overtime (last 5 days) + for dic in topSortedOrg[0:5]: + org = dic['org'] + overtime = [] + for deltaD in range(1,6,1): + date = (datetime.datetime(today.year, today.month, today.day) - datetime.timedelta(days=deltaD)) + keyname = 'CONTRIB_DAY:'+getDateStrFormat(date) + org_score = serv_redis_db.zscore(keyname, org) + if org_score is None: + org_score = 0 + overtime.append([deltaD, org_score]) + to_append = {'label': org, 'data': overtime} + data.append(to_append) return jsonify(data) + #return jsonify(data2) @app.route("/_getCategPerContrib") def getCategPerContrib(): - data = [] + data2 = [] for d in range(15): dic = {} dic['rank'] = random.randint(1,16) @@ -351,14 +471,40 @@ def getCategPerContrib(): dic['org'] = 'Org'+str(d) for f in categories_in_datatable: dic[f] = random.randint(0,1600) + data2.append(dic) + + try: + date = datetime.datetime.fromtimestamp(float(request.args.get('date'))) + except: + date = datetime.datetime.now() + keyCateg = "CONTRIB_DAY" + topNum = 0 # all + contrib_org = getZrange(keyCateg, date, topNum) + data = [] + for org, pnts in contrib_org: + dic = {} + dic['rank'] = getTrueRank(pnts) + dic['logo_path'] = getOrgLogoFromRedis(org) + dic['org'] = org + dic['pnts'] = pnts + for categ in categories_in_datatable: + keyname = 'CONTRIB_CATEG:'+getDateStrFormat(date)+':'+categ + categ_score = serv_redis_db.zscore(keyname, org) + if categ_score is None: + categ_score = 0 + dic[categ] = categ_score data.append(dic) return jsonify(data) + return jsonify(data2) @app.route("/_getAllOrg") def getAllOrg(): - data = ['CIRCL', 'CASES', 'SMILE' ,'ORG4' ,'ORG5', 'SUPER HYPER LONG ORGINZATION NAME', 'Org3'] + data = serv_redis_db.smembers('CONTRIB_ALL_ORG') + data = [x.decode('utf8') for x in data] + data2 = ['CIRCL', 'CASES', 'SMILE' ,'ORG4' ,'ORG5', 'SUPER HYPER LONG ORGINZATION NAME', 'Org3'] return jsonify(data) + #return jsonify(data2) @app.route("/_getOrgRank") def getOrgRank(): @@ -366,7 +512,9 @@ def getOrgRank(): org = request.args.get('org') except: org = '' + date = datetime.datetime.now() points = random.randint(1,math.floor(cfg.getfloat('CONTRIB' ,'rankMultiplier')**16)) + points = getOrgPntFromRedis(org, date) #FIXME put 0 if org has no points remainingPts = getRemainingPoints(points) data = {'org': org, diff --git a/static/js/contrib.js b/static/js/contrib.js index 123766e..38bbfb4 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -197,7 +197,7 @@ function addToTableFromJson(datatable, url) { var row = data[i]; i = parseInt(i); var to_add = [ - i+1, + row.pnts, getRankIcon(row.rank), row.logo_path, row.org @@ -211,7 +211,6 @@ function addToTableFromJson(datatable, url) { function updateProgressHeader(org) { // get Org rank $.getJSON( url_getOrgRank+'?org='+org, function( data ) { - console.log(data); datatableTop.draw(); var rank = Math.floor(data.rank); var rankDec = data.rank-rank; @@ -262,9 +261,9 @@ $(document).ready(function() { // top contributors addToTableFromJson(datatableTop, url_getTopContributor); // hall of fame - addToTableFromJson(datatableFame, url_getTopContributor); + addToTableFromJson(datatableFame, url_getFameContributor); // last contributors - addToTableFromJson(datatableLast, url_getTopContributor); + addToTableFromJson(datatableLast, url_getLastContributor); // category per contributors $.getJSON( url_getCategPerContrib, function( data ) { for (i in data) { @@ -286,6 +285,7 @@ $(document).ready(function() { }); // top 5 contrib overtime $.getJSON( url_getTop5Overtime, function( data ) { + console.log(data); var plotLineChart = $.plot("#divTop5Overtime", data, optionsLineChart); }); }); diff --git a/templates/contrib.html b/templates/contrib.html index 1dcb8e6..ad36e6b 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -99,7 +99,7 @@
    #prev. rank Logo Organization name
    # Org rank LogoOrganization nameNetwork ActivityPayload deliveryOthers{{ categ }}
    #Org rankRank LogoOrganization nameOrganisation
    #Org rankRank LogoOrg nameOrganisation
    #Org rankRank LogoOrganisation{{ categ }}{{ categ }}
    #prev. rankPrev. rank LogoOrganization nameOrganisation
    - + @@ -126,7 +126,7 @@
    #Points Rank Logo Organisation
    - + @@ -152,7 +152,7 @@
    #Points Rank Logo Organisation
    - + @@ -187,7 +187,7 @@
    #Points Rank Logo Organisation
    - + @@ -229,8 +229,10 @@ From a337ee1a853541eae66ad1693327811b8a0beab4 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 7 Nov 2017 12:59:32 +0100 Subject: [PATCH 40/65] Added pic centering in lastDataTable --- static/js/contrib.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/static/js/contrib.js b/static/js/contrib.js index 67935f8..c8dd1c5 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -57,6 +57,8 @@ var optionDatatable_light = { var optionDatatable_top = jQuery.extend({}, optionDatatable_light) var optionDatatable_last = jQuery.extend({}, optionDatatable_light) optionDatatable_last.columnDefs = [ + { className: "centerCellPicOrgRank", "targets": [ 2 ] }, + { className: "centerCellPicOrgLogo", "targets": [ 3 ] }, { 'orderData':[5], 'targets': [0] }, { 'targets': [5], From 90a518cc071cc68e7fa89fbdc9829a5d27adbb0c Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 8 Nov 2017 11:29:07 +0100 Subject: [PATCH 41/65] Added support of honorBadge + Added text related to global ranking requirements --- contributor_helper.py | 31 +++- server.py | 13 +- static/css/ranking.css | 13 +- static/js/contrib.js | 43 ++++-- static/pics/MISPHonorableIcons/1.svg | 178 ++++++++++++++++++++++ static/pics/MISPHonorableIcons/2.svg | 185 +++++++++++++++++++++++ static/pics/rankingMISPOrg/14.svg | 86 +++++------ static/pics/rankingMISPOrg/15.svg | 218 +++++++++------------------ static/pics/rankingMISPOrg/16.svg | 91 +++++++---- templates/contrib.html | 63 ++++++-- zmq_subscriber.py | 50 +++++- 11 files changed, 713 insertions(+), 258 deletions(-) create mode 100644 static/pics/MISPHonorableIcons/1.svg create mode 100644 static/pics/MISPHonorableIcons/2.svg diff --git a/contributor_helper.py b/contributor_helper.py index 530c4a7..6252302 100644 --- a/contributor_helper.py +++ b/contributor_helper.py @@ -10,16 +10,32 @@ class Contributor_helper: self.serv_redis_db = serv_redis_db self.cfg = cfg self.cfg_org_rank = configparser.ConfigParser() - self.cfg_org_rank.read(os.path.join(os.environ['DASH_CONFIG'], 'rankTitle.cfg')) + self.cfg_org_rank.read(os.path.join(os.environ['DASH_CONFIG'], 'ranking.cfg')) + #honorBadge + self.org_honor_badge_title = {} + for badgeNum in range(1, len(self.cfg_org_rank.options('HonorBadge'))+1): #get Num of honorBadge + self.org_honor_badge_title[badgeNum] = self.cfg_org_rank.get('HonorBadge', str(badgeNum)) + + #GLOBAL RANKING self.org_rank_maxLevel = self.cfg_org_rank.getint('rankTitle', 'maxLevel') self.org_rank = {} for rank in range(1, self.org_rank_maxLevel+1): self.org_rank[rank] = self.cfg_org_rank.get('rankTitle', str(rank)) + self.org_rank_requirement_pnts = {} + for rank in range(1, self.org_rank_maxLevel+1): + self.org_rank_requirement_pnts[rank] = self.cfg_org_rank.getint('rankRequirementsPnts', str(rank)) + self.org_rank_requirement_text = {} + for rank in range(1, self.org_rank_maxLevel+1): + self.org_rank_requirement_text[rank] = self.cfg_org_rank.get('rankRequirementsText', str(rank)) + self.org_rank_additional_info = json.loads(self.cfg_org_rank.get('additionalInfo', 'textsArray')) + #WEB STUFF self.misp_web_url = cfg.get('RedisGlobal', 'misp_web_url') self.MAX_NUMBER_OF_LAST_CONTRIBUTOR = cfg.getint('CONTRIB', 'max_number_of_last_contributor') self.categories_in_datatable = json.loads(cfg.get('CONTRIB', 'categories_in_datatable')) + + #MONTHLY RANKING self.default_pnts_per_contribution = json.loads(cfg.get('CONTRIB', 'default_pnts_per_contribution')) temp = json.loads(cfg.get('CONTRIB', 'pnts_per_contribution')) self.DICO_PNTS_REWARD = {} @@ -196,6 +212,7 @@ class Contributor_helper: dic = {} dic['rank'] = random.randint(1,self.levelMax) dic['orgRank'] = random.randint(1,self.levelMax), + dic['honorBadge'] = [random.randint(1,2)], dic['logo_path'] = 'logo' dic['org'] = 'Org'+str(d) dic['pnts'] = random.randint(1,2**self.levelMax) @@ -216,6 +233,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [1,2], 'logo_path': self.getOrgLogoFromRedis('MISP'), 'org': 'MISP', 'pnts': random.randint(1,2**self.levelMax) @@ -223,6 +241,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [1], 'logo_path': 'logo1', 'org': 'CIRCL', 'pnts': random.randint(1,2**self.levelMax) @@ -230,6 +249,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [2], 'logo_path': 'logo2', 'org': 'CASES', 'pnts': random.randint(1,2**self.levelMax) @@ -237,6 +257,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [], 'logo_path': 'logo3', 'org': 'SMILE', 'pnts': random.randint(1,2**self.levelMax) @@ -244,6 +265,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [], 'logo_path': 'logo4', 'org': 'ORG4', 'pnts': random.randint(1,2**self.levelMax) @@ -251,6 +273,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [], 'logo_path': 'logo5', 'org': 'ORG5', 'pnts': random.randint(1,2**self.levelMax) @@ -264,6 +287,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [1,2], 'logo_path': self.getOrgLogoFromRedis('MISP'), 'org': 'MISP', 'pnts': random.randint(1,2**self.levelMax), @@ -272,6 +296,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [1], 'logo_path': 'logo1', 'org': 'CIRCL', 'pnts': random.randint(1,2**self.levelMax), @@ -280,6 +305,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [2], 'logo_path': 'logo2', 'org': 'CASES', 'pnts': random.randint(1,2**self.levelMax), @@ -288,6 +314,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [], 'logo_path': 'logo3', 'org': 'SMILE', 'pnts': random.randint(1,2**self.levelMax), @@ -296,6 +323,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [], 'logo_path': 'logo4', 'org': 'ORG4', 'pnts': random.randint(1,2**self.levelMax), @@ -304,6 +332,7 @@ class Contributor_helper: { 'rank': random.randint(1,self.levelMax), 'orgRank': random.randint(1,self.levelMax), + 'honorBadge': [], 'logo_path': 'logo5', 'org': 'ORG5', 'pnts': random.randint(1,2**self.levelMax), diff --git a/server.py b/server.py index 00a867d..0cc30ab 100755 --- a/server.py +++ b/server.py @@ -151,9 +151,17 @@ 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_points = [contributor_helper.DICO_PNTS_REWARD[categ] for categ in categ_list] + org_rank = contributor_helper.org_rank - org_rank_list = [[rank, title] for rank, title in org_rank.items()] + org_rank_requirement_pnts = contributor_helper.org_rank_requirement_pnts + org_rank_requirement_text = contributor_helper.org_rank_requirement_text + org_rank_list = [[rank, title, org_rank_requirement_pnts[rank], org_rank_requirement_text[rank]] for rank, title in org_rank.items()] org_rank_list.sort(key=lambda x: x[0]) + org_rank_additional_text = contributor_helper.org_rank_additional_info + + org_honor_badge_title = contributor_helper.org_honor_badge_title + 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]) currOrg = request.args.get('org') if currOrg is None: @@ -168,6 +176,9 @@ def contrib(): categ_list_points=categ_list_points, org_rank_json=json.dumps(org_rank), org_rank_list=org_rank_list, + 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, min_between_reload=cfg.getint('CONTRIB', 'min_between_reload') ) diff --git a/static/css/ranking.css b/static/css/ranking.css index f29aac4..24f5d8d 100644 --- a/static/css/ranking.css +++ b/static/css/ranking.css @@ -3,6 +3,15 @@ height: auto; } +.circleBadge { + width: 80px; + height: 80px; + text-align: center; + border-radius: 65px; + background-color: #e1e1e1; + border: 1px solid #caccce; +} + .questionBadgeText { left: -15%; position: relative; @@ -27,8 +36,8 @@ left: 1%; } -.infoBlue { - background-color: #d1d1d1 !important; +.selectedOrgInTable { + background-color: #fff6c2 !important; color: black; } diff --git a/static/js/contrib.js b/static/js/contrib.js index c8dd1c5..e29cdae 100644 --- a/static/js/contrib.js +++ b/static/js/contrib.js @@ -51,7 +51,8 @@ var optionDatatable_light = { "info": false, "columnDefs": [ { className: "centerCellPicOrgRank", "targets": [ 2 ] }, - { className: "centerCellPicOrgLogo", "targets": [ 3 ] } + { className: "centerCellPicOrgLogo", "targets": [ 3 ] }, + { className: "centerCellPicOrgLogo", "targets": [ 4 ] } ] }; var optionDatatable_top = jQuery.extend({}, optionDatatable_light) @@ -59,9 +60,10 @@ var optionDatatable_last = jQuery.extend({}, optionDatatable_light) optionDatatable_last.columnDefs = [ { className: "centerCellPicOrgRank", "targets": [ 2 ] }, { className: "centerCellPicOrgLogo", "targets": [ 3 ] }, - { 'orderData':[5], 'targets': [0] }, + { className: "centerCellPicOrgLogo", "targets": [ 4 ] }, + { 'orderData':[6], 'targets': [0] }, { - 'targets': [5], + 'targets': [6], 'visible': false, 'searchable': false }, @@ -79,7 +81,8 @@ var optionDatatable_Categ = { "info": false, "columnDefs": [ { className: "centerCellPicOrgRank", "targets": [ 2 ] }, - { className: "centerCellPicOrgLogo", "targets": [ 3 ], 'searchable': false, 'sortable': false } + { className: "centerCellPicOrgLogo", "targets": [ 3 ], 'searchable': false, 'sortable': false }, + { className: "centerCellPicOrgLogo", "targets": [ 4 ]} ] }; @@ -144,16 +147,30 @@ function getOrgRankIcon(rank, size) { return obj.outerHTML; } -function createImg(source, size, return_obj) { +function createImg(source, size) { var obj = document.createElement('object'); obj.height = size; obj.width = size; obj.style.margin = 'auto'; obj.data = source; - obj.type = "image/png" return obj.outerHTML; } +function createHonorImg(array, size) { + size = 32; + var div = document.createElement('div'); + for (badgeNum of array) { + var obj = document.createElement('img'); + obj.height = size; + obj.width = size; + obj.style.margin = 'auto'; + obj.title = org_honor_badge_title[badgeNum]; + obj.src = url_baseHonorLogo+badgeNum+'.svg'; + div.appendChild(obj); + } + return div.outerHTML; +} + function generateRankingSheet(rank, rankDec, stepPnt, pnt, Rpnt) { var Cpnt = pnt - stepPnt; var Tpnt = Cpnt + Rpnt; @@ -241,7 +258,6 @@ function generateRankingSheet(rank, rankDec, stepPnt, pnt, Rpnt) { function addToTableFromJson(datatable, url) { $.getJSON( url, function( data ) { - console.log(data); for (i in data) { var row = data[i]; i = parseInt(i); @@ -249,6 +265,7 @@ function addToTableFromJson(datatable, url) { row.pnts, getMonthlyRankIcon(row.rank), getOrgRankIcon(row.orgRank, 60), + createHonorImg(row.honorBadge, 20), createImg(row.logo_path, 32), row.org ]; @@ -274,6 +291,7 @@ function addLastContributor(datatable, data, update) { data.pnts, getMonthlyRankIcon(data.rank), getOrgRankIcon(data.orgRank, 60), + createHonorImg(data.honorBadge, 20), createImg(data.logo_path, 32), data.org, data.epoch @@ -309,19 +327,19 @@ function updateProgressHeader(org) { // update color in other dataTables datatableTop.rows().every( function() { var row = this.node(); - if(this.data()[3] == data.org) { row.classList.add('infoBlue'); } else { row.classList.remove('infoBlue'); } + if(this.data()[5] == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); } }); datatableFame.rows().every( function() { var row = this.node(); - if(this.data()[3] == data.org) { row.classList.add('infoBlue'); } else { row.classList.remove('infoBlue'); } + if(this.data()[5] == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); } }); datatableCateg.rows().every( function() { var row = this.node(); - if(this.data()[3] == data.org) { row.classList.add('infoBlue'); } else { row.classList.remove('infoBlue'); } + if(this.data()[5] == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); } }); datatableLast.rows().every( function() { var row = this.node(); - if(this.data()[3] == data.org) { row.classList.add('infoBlue'); } else { row.classList.remove('infoBlue'); } + if(this.data()[5] == data.org) { row.classList.add('selectedOrgInTable'); } else { row.classList.remove('selectedOrgInTable'); } }); }); } @@ -381,7 +399,8 @@ $(document).ready(function() { row.pnts, getMonthlyRankIcon(row.rank), getOrgRankIcon(row.orgRank, 44), - row.logo_path, + createHonorImg(row.honorBadge, 20), + createImg(row.logo_path, 32), row.org, ]; for (categ of categ_list) { diff --git a/static/pics/MISPHonorableIcons/1.svg b/static/pics/MISPHonorableIcons/1.svg new file mode 100644 index 0000000..16de876 --- /dev/null +++ b/static/pics/MISPHonorableIcons/1.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/static/pics/MISPHonorableIcons/2.svg b/static/pics/MISPHonorableIcons/2.svg new file mode 100644 index 0000000..65bcf30 --- /dev/null +++ b/static/pics/MISPHonorableIcons/2.svg @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/static/pics/rankingMISPOrg/14.svg b/static/pics/rankingMISPOrg/14.svg index 3051dc1..abcfb78 100644 --- a/static/pics/rankingMISPOrg/14.svg +++ b/static/pics/rankingMISPOrg/14.svg @@ -15,7 +15,7 @@ version="1.1" id="svg8" inkscape:version="0.92.1 r15371" - sodipodi:docname="14-General.svg"> + sodipodi:docname="14.svg"> - - - - - - - - - - - + + + + + + + diff --git a/static/pics/rankingMISPOrg/15.svg b/static/pics/rankingMISPOrg/15.svg index f2782eb..ff4f422 100644 --- a/static/pics/rankingMISPOrg/15.svg +++ b/static/pics/rankingMISPOrg/15.svg @@ -15,7 +15,7 @@ version="1.1" id="svg8" inkscape:version="0.92.1 r15371" - sodipodi:docname="15-Marshal.svg"> + sodipodi:docname="15.svg"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/static/pics/rankingMISPOrg/16.svg b/static/pics/rankingMISPOrg/16.svg index 4ace1cb..670e901 100644 --- a/static/pics/rankingMISPOrg/16.svg +++ b/static/pics/rankingMISPOrg/16.svg @@ -15,7 +15,7 @@ version="1.1" id="svg8" inkscape:version="0.92.1 r15371" - sodipodi:docname="16-Grand General.svg"> + sodipodi:docname="16.svg"> - - - - - - + id="g3760" + transform="matrix(0.26458333,0,0,0.26458333,0.25014905,248.81803)"> + + + + + + + + + + + + + diff --git a/templates/contrib.html b/templates/contrib.html index dbc3c5a..7e56ca2 100644 --- a/templates/contrib.html +++ b/templates/contrib.html @@ -46,23 +46,24 @@
    #Points Prev. rank Logo Organisation
    @@ -83,26 +84,53 @@
    -
    -

    Organisation rank title:

    +
    +

    Global organisation ranking:

    + + {% for item in org_rank_list %} - + + + {% endfor %}
    Org. rank TitlePoints requirementContribution requirement
    {{ item[1] }}{{ item[2] }}{{ item[3] }}
    +
    +
      + {% for text in org_rank_additional_text %} +
    • {{ text }}
    • + {% endfor %} +
    +
    +
    + + + {% for item in org_honor_badge_title_list %} + + + + + {% endfor %} + +
    +
    + +
    +
    {{ item[1] }}
    +
    @@ -166,7 +194,7 @@
    -
    +
    Contributor Ranking @@ -181,6 +209,7 @@ Cur. rank Org. rank + Organisation @@ -194,7 +223,7 @@
    -
    +
    Last contributors @@ -209,6 +238,7 @@ Cur. rank Org. rank + Organisation epoch @@ -237,6 +267,7 @@ Cur. rank Org. rank + Organisation {% for categ in categ_list_str %} {{ categ }} @@ -273,6 +304,7 @@ Prev. rank Org. rank + Organisation @@ -323,6 +355,8 @@ 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); /* DATA FROM CONF */ var currOrg = "{{ currOrg }}"; @@ -330,6 +364,7 @@ var rankMultiplier = {{ rankMultiplier }}; var categ_list = JSON.parse('{{ categ_list|safe }}'); var org_rank_obj = JSON.parse('{{ org_rank_json|safe }}'); + var org_honor_badge_title = JSON.parse('{{ org_honor_badge_title|safe }}'); diff --git a/zmq_subscriber.py b/zmq_subscriber.py index 1421ca6..316461d 100755 --- a/zmq_subscriber.py +++ b/zmq_subscriber.py @@ -18,6 +18,7 @@ configfile = os.path.join(os.environ['DASH_CONFIG'], 'config.cfg') cfg = configparser.ConfigParser() cfg.read(configfile) +ONE_DAY = 60*60*24 ZMQ_URL = cfg.get('RedisGlobal', 'zmq_url') CHANNEL = cfg.get('RedisLog', 'channel') CHANNEL_LASTCONTRIB = cfg.get('RedisLog', 'channelLastContributor') @@ -142,10 +143,57 @@ def handleContribution(zmq_name, org, categ, action, pntMultiplier=1): now = datetime.datetime.now() nowSec = int(time.time()) serv_redis_db.zadd('CONTRIB_LAST:'+getDateStrFormat(now), nowSec, org) - serv_redis_db.expire('CONTRIB_LAST:'+getDateStrFormat(now), 60*60*24) #expire after 1 day + serv_redis_db.expire('CONTRIB_LAST:'+getDateStrFormat(now), ONE_DAY) #expire after 1 day + + updateOrgRank(org, pnts_to_add, eventTime, eventClassification) publish_log(zmq_name, 'CONTRIBUTION', {'org': org, 'categ': categ, 'action': action, 'epoch': nowSec }, channel=CHANNEL_LASTCONTRIB) +def updateOrgRank(orgName, pnts_to_add, contribType, eventTime, isClassified): + keyname = 'CONTRIB_ORG:{org}:{orgCateg}' + #update total points + serv_redis_db.set(keyname.format(org=orgName, orgCateg='points'), pnts_to_add) + #update contribution Requirement + heavilyCount = 10 + recentDays = 31 + regularlyDays = 7 + isRecent = True if (datetime.datetime.now() - eventTime).days > recentDays + contrib = [] #[[contrib_level, contrib_ttl], [], ...] + if contribType == 'sighting': + #[contrib_level, contrib_ttl] + contrib.append([1, ONE_DAY*365]]) + if contribType == 'attribute' or contribType == 'object': + contrib.append([2, ONE_DAY*365]) + if contribType == 'proposal' or contribType == 'discussion': + contrib.append([3, ONE_DAY*365]) + if contribType == 'sighting' and isRecent: + contrib.append([4, ONE_DAY*recentDays]) + if contribType == 'proposal' and isRecent: + contrib.append([5, ONE_DAY*recentDays]) + if contribType == 'event': + contrib.append([6, ONE_DAY*365]) + if contribType == 'event': + contrib.append([7, ONE_DAY*recentDays]) + if contribType == 'event': + contrib.append([8, ONE_DAY*regularlyDays]) + if contribType == 'event' and isClassified: + contrib.append([9, ONE_DAY*regularlyDays]) + if contribType == 'sighting' and sightingWeekCount>heavilyCount: + contrib.append([10, ONE_DAY*regularlyDays]) + if (contribType == 'attribute' or contribType == 'object') and attributeWeekCount>heavilyCount: + contrib.append([11, ONE_DAY*regularlyDays]) + if contribType == 'proposal' and proposalWeekCount>heavilyCount: + contrib.append([12, ONE_DAY*regularlyDays]) + if contribType == 'event' and eventWeekCount>heavilyCount: + contrib.append([13, ONE_DAY*regularlyDays]) + if contribType == 'event' and eventWeekCount>heavilyCount and isClassified: + contrib.append([14, ONE_DAY*regularlyDays]) + + for rankReq, ttl: + serv_redis_db.set(keyname.format(org=orgName, orgCateg='CONTRIB_REQ_'+str(rankReq)), 1) + serv_redis_db.expire(keyname.format(org=orgName, orgCateg='CONTRIB_REQ_'+str(i)), ttl) + + ############## ## HANDLERS ## ############## From a9d5ca5657350ebd3b76fb26ffd1808c409d8bdd Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 8 Nov 2017 11:35:40 +0100 Subject: [PATCH 42/65] Updated configuration --- config/config.cfg.default | 6 ++-- config/ranking.cfg | 65 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 config/ranking.cfg diff --git a/config/config.cfg.default b/config/config.cfg.default index 738b175..8bfee3e 100644 --- a/config/config.cfg.default +++ b/config/config.cfg.default @@ -38,13 +38,15 @@ char_separator=|| [RedisGlobal] host=localhost port=6251 +#misp_web_url = http://192.168.56.50 +misp_web_url = http://localhost +#zmq_url=tcp://192.168.56.50:50000 +zmq_url=tcp://localhost:50000 [RedisLog] db=0 channel=1 channelLastContributor = lastContributor -zmq_url=tcp://localhost:50000 -#zmq_url=tcp://192.168.56.50:50000 [RedisMap] db=1 diff --git a/config/ranking.cfg b/config/ranking.cfg new file mode 100644 index 0000000..32788aa --- /dev/null +++ b/config/ranking.cfg @@ -0,0 +1,65 @@ +[rankTitle] +maxLevel=16 +1=Ensign +2=Junior Lieutenant +3=Second Lieutenant +4=Lieutenant +5=Senior Lieutenant +6=Captain +7=Senior Captain +8=Major +9=Lieutenant Colonel +10=Colonel +11=Senior Colonel +12=Major General +13=Lieutenant General +14=General +15=Marshal +16=Grand General + +[rankRequirementsPnts] +1=2 +2=4 +3=8 +4=16 +5=32 +6=64 +7=128 +8=256 +9=512 +10=1024 +11=2048 +12=4096 +13=8192 +14=16384 +15=32768 +16=65536 + +[rankRequirementsText] +1=Contributing via sighting at least once a year +2=Contributing via attributes or objects to events at least once a year +3=Contributing via proposals or discussions at least once a year + +4=Contributing via sighting to recent events +5=Contributing proposals to recent events + +6=Contributing by creating new events at least once a year +7=Contributing by creating new events at least once a month +8=Regularly creating events +9=Regularly creating events with classification + +10=Heavily contributing via sighting to recent events +11=Heavily adding attributes or objects to recent events +12=Heavily contributing proposals to recent events +13=Heavily posting events +14=Heavily posting events with classification + +15=Heavily contributing quality content (delivered by instance administrators) +16=Honorable contributor (delivered by instance administrators) + +[HonorBadge] +1=Has made at least one pull request on the MISP project +2=Is a donator for the MISP project + +[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"] From 605538ce976c0124fa4588f5bdbb82458d9d8912 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 8 Nov 2017 15:34:02 +0100 Subject: [PATCH 43/65] Added display of conribution rank statys + acquired badges --- config/ranking.cfg | 5 ++ contributor_helper.py | 119 +++++++++++++++++++++++++++++++++++++++-- server.py | 18 ++++++- static/css/ranking.css | 4 ++ static/js/contrib.js | 35 ++++++++++++ templates/contrib.html | 9 ++-- zmq_subscriber.py | 12 +++-- 7 files changed, 191 insertions(+), 11 deletions(-) diff --git a/config/ranking.cfg b/config/ranking.cfg index 32788aa..b0119fb 100644 --- a/config/ranking.cfg +++ b/config/ranking.cfg @@ -35,6 +35,11 @@ maxLevel=16 15=32768 16=65536 +[rankRequirementsMisc] +heavilyCount=10 +recentDays=31 +regularlyDays=7 + [rankRequirementsText] 1=Contributing via sighting at least once a year 2=Contributing via attributes or objects to events at least once a year diff --git a/contributor_helper.py b/contributor_helper.py index 6252302..ee1cad4 100644 --- a/contributor_helper.py +++ b/contributor_helper.py @@ -13,8 +13,13 @@ class Contributor_helper: self.cfg_org_rank.read(os.path.join(os.environ['DASH_CONFIG'], 'ranking.cfg')) #honorBadge + self.honorBadgeNum = len(self.cfg_org_rank.options('HonorBadge')) + self.heavilyCount = self.cfg_org_rank.getint('rankRequirementsMisc', 'heavilyCount') + self.recentDays = self.cfg_org_rank.getint('rankRequirementsMisc', 'recentDays') + self.regularlyDays = self.cfg_org_rank.getint('rankRequirementsMisc', 'regularlyDays') + self.org_honor_badge_title = {} - for badgeNum in range(1, len(self.cfg_org_rank.options('HonorBadge'))+1): #get Num of honorBadge + 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)) #GLOBAL RANKING @@ -51,6 +56,11 @@ class Contributor_helper: self.rankMultiplier = cfg.getfloat('CONTRIB' ,'rankMultiplier') self.levelMax = 16 + + ''' HELPER ''' + def getOrgLogoFromRedis(self, org): + return "{}/img/orgs/{}.png".format(self.misp_web_url, org) + def getZrange(self, keyCateg, date, topNum, endSubkey=""): date_str = util.getDateStrFormat(date) keyname = "{}:{}{}".format(keyCateg, date_str, endSubkey) @@ -58,6 +68,64 @@ class Contributor_helper: data = [ [record[0].decode('utf8'), record[1]] for record in data ] return data + ''' CONTRIBUTION RANK ''' + # return: [final_rank, requirement_fulfilled, requirement_not_fulfilled] + def getOrgContributionRank(self, org): + keyname = 'CONTRIB_ORG:{org}:{orgCateg}' + final_rank = 0 + requirement_fulfilled = [] + requirement_not_fulfilled = [] + for i in range(1, self.org_rank_maxLevel+1): + key = keyname.format(org=org, orgCateg='CONTRIB_REQ_'+str(i)) + if self.serv_redis_db.get(key) is None: #non existing + requirement_not_fulfilled.append(i) + else: + requirement_fulfilled.append(i) + final_rank += 1 + num_of_previous_req_not_fulfilled = len([x for x in requirement_not_fulfilled if xContribution requirement - + {% for item in org_rank_list %} - + {{ item[1] }} {{ item[2] }} @@ -121,7 +121,7 @@ {% for item in org_honor_badge_title_list %} -
    +
    @@ -171,6 +171,7 @@
    - -