mirror of https://github.com/MISP/misp-dashboard
feature: Added draft of timeline which shows event overtime. The event
currently span consecutive days (if it has been seen).pull/29/head
parent
ef6f9190b2
commit
5de0d31818
|
@ -158,3 +158,43 @@ class Trendings_helper:
|
||||||
tagSet.add(tag['name'])
|
tagSet.add(tag['name'])
|
||||||
to_ret[self.keyTag] = list(tagSet)
|
to_ret[self.keyTag] = list(tagSet)
|
||||||
return to_ret
|
return to_ret
|
||||||
|
|
||||||
|
# In contrary of getGenericTrending, it regroups items in the format: {item, start: timestamp1, end: timestamp2}
|
||||||
|
# so that it can be displayed easily on the timeline.
|
||||||
|
def getGenericTrendingOvertime(self, dateS, dateE, trendingType=None, topNum=0):
|
||||||
|
trendingType = self.keyEvent
|
||||||
|
dico_items = {}
|
||||||
|
to_format = []
|
||||||
|
prev_days = (dateE - dateS).days
|
||||||
|
# get data
|
||||||
|
for curDate in util.getXPrevDaysSpan(dateE, prev_days):
|
||||||
|
keyname = "{}:{}".format(trendingType, util.getDateStrFormat(curDate))
|
||||||
|
data = self.serv_redis_db.zrange(keyname, 0, topNum-1, desc=True, withscores=True)
|
||||||
|
data = [ [record[0].decode('utf8'), record[1]] for record in data ]
|
||||||
|
data = data if data is not None else []
|
||||||
|
to_format.append([util.getTimestamp(curDate), data])
|
||||||
|
|
||||||
|
for timestamp, array in to_format:
|
||||||
|
for item, _ in array:
|
||||||
|
if item not in dico_items:
|
||||||
|
dico_items[item] = []
|
||||||
|
dico_items[item].append(timestamp)
|
||||||
|
|
||||||
|
# sort timestamps in correct order
|
||||||
|
for item in dico_items.keys():
|
||||||
|
dico_items[item].sort()
|
||||||
|
# dico_items have the form: {item: [t1,t2,t4], ...}
|
||||||
|
to_ret = []
|
||||||
|
ONEDAY = 60*60*24
|
||||||
|
for item, timestamps in dico_items.items():
|
||||||
|
obj = {'name': item, 'start': timestamps[0]-ONEDAY, 'end': timestamps[0]}
|
||||||
|
for t in timestamps:
|
||||||
|
if t-obj['end'] > ONEDAY: #new entry
|
||||||
|
to_ret.append(obj)
|
||||||
|
obj['start'] = t-ONEDAY
|
||||||
|
obj['end'] = t
|
||||||
|
else: # contrinue entry
|
||||||
|
obj['end'] = t
|
||||||
|
to_ret.append(obj)
|
||||||
|
|
||||||
|
return to_ret
|
||||||
|
|
|
@ -108,4 +108,9 @@ mv temp/jquery-punchcard/src/punchcard.js ./static/js
|
||||||
mv temp/jquery-punchcard/src/punchcard.css ./static/css
|
mv temp/jquery-punchcard/src/punchcard.css ./static/css
|
||||||
wget https://momentjs.com/downloads/moment.js -O ./static/js/moment.js
|
wget https://momentjs.com/downloads/moment.js -O ./static/js/moment.js
|
||||||
|
|
||||||
|
# timeline
|
||||||
|
VISJS_VERSION="4.21.0"
|
||||||
|
https://cdnjs.cloudflare.com/ajax/libs/vis/${VISJS_VERSION}/vis.min.js
|
||||||
|
https://cdnjs.cloudflare.com/ajax/libs/vis/${VISJS_VERSION}/vis.min.css
|
||||||
|
|
||||||
rm -rf ./temp
|
rm -rf ./temp
|
||||||
|
|
12
server.py
12
server.py
|
@ -536,5 +536,17 @@ def getTypeaheadData():
|
||||||
data = trendings_helper.getTypeaheadData(dateS, dateE)
|
data = trendings_helper.getTypeaheadData(dateS, dateE)
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
|
@app.route("/_getGenericTrendingOvertime")
|
||||||
|
def getGenericTrendingOvertime():
|
||||||
|
try:
|
||||||
|
dateS = datetime.datetime.fromtimestamp(float(request.args.get('dateS')))
|
||||||
|
dateE = datetime.datetime.fromtimestamp(float(request.args.get('dateE')))
|
||||||
|
except:
|
||||||
|
dateS = datetime.datetime.now() - datetime.timedelta(days=7)
|
||||||
|
dateE = datetime.datetime.now()
|
||||||
|
|
||||||
|
data = trendings_helper.getGenericTrendingOvertime(dateS, dateE)
|
||||||
|
return jsonify(data)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(host='localhost', port=8001, threaded=True)
|
app.run(host='localhost', port=8001, threaded=True)
|
||||||
|
|
|
@ -9,6 +9,7 @@ var tagPie = ["#tagPie"];
|
||||||
var tagLine = ["#tagLine"];
|
var tagLine = ["#tagLine"];
|
||||||
var sightingLineWidget;
|
var sightingLineWidget;
|
||||||
var discLine = ["#discussionLine"];
|
var discLine = ["#discussionLine"];
|
||||||
|
var timeline;
|
||||||
var allData;
|
var allData;
|
||||||
var globalColorMapping = {};
|
var globalColorMapping = {};
|
||||||
|
|
||||||
|
@ -103,6 +104,8 @@ var typeaheadOption_tag = {
|
||||||
updateLineForLabel(tagLine, tag, undefined, url_getTrendingTag);
|
updateLineForLabel(tagLine, tag, undefined, url_getTrendingTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var timeline_option = {};
|
||||||
|
|
||||||
|
|
||||||
/* FUNCTIONS */
|
/* FUNCTIONS */
|
||||||
function getColor(label) {
|
function getColor(label) {
|
||||||
|
@ -396,6 +399,29 @@ function updateDisc() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTimeline() {
|
||||||
|
$.getJSON( url_getGenericTrendingOvertime+"?dateS="+parseInt(dateStart.getTime()/1000)+"&dateE="+parseInt(dateEnd.getTime()/1000), function( data ) {
|
||||||
|
var items = [];
|
||||||
|
var i = 1;
|
||||||
|
for (var obj of data) {
|
||||||
|
if (obj.end == obj.start) { console.log(obj);}
|
||||||
|
items.push({
|
||||||
|
id: i,
|
||||||
|
content: obj.name,
|
||||||
|
start: obj.start*1000,
|
||||||
|
end: obj.end*1000
|
||||||
|
});
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
items = new vis.DataSet(items);
|
||||||
|
if (timeline === undefined) { // create timeline
|
||||||
|
timeline = new vis.Timeline(document.getElementById('timeline'), items, timeline_option);
|
||||||
|
} else { // update
|
||||||
|
timeline.setItems(items);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function dateChanged() {
|
function dateChanged() {
|
||||||
dateStart = datePickerWidgetStart.datepicker( "getDate" );
|
dateStart = datePickerWidgetStart.datepicker( "getDate" );
|
||||||
dateEnd = datePickerWidgetEnd.datepicker( "getDate" );
|
dateEnd = datePickerWidgetEnd.datepicker( "getDate" );
|
||||||
|
@ -404,6 +430,7 @@ function dateChanged() {
|
||||||
updatePieLine(tagPie, tagLine, url_getTrendingTag);
|
updatePieLine(tagPie, tagLine, url_getTrendingTag);
|
||||||
updateSignthingsChart();
|
updateSignthingsChart();
|
||||||
updateDisc();
|
updateDisc();
|
||||||
|
updateTimeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
@ -426,6 +453,7 @@ $(document).ready(function () {
|
||||||
updatePieLine(tagPie, tagLine, url_getTrendingTag)
|
updatePieLine(tagPie, tagLine, url_getTrendingTag)
|
||||||
updateSignthingsChart();
|
updateSignthingsChart();
|
||||||
updateDisc();
|
updateDisc();
|
||||||
|
updateTimeline();
|
||||||
|
|
||||||
$( "#num_selector" ).change(function() {
|
$( "#num_selector" ).change(function() {
|
||||||
var sel = parseInt($( this ).val());
|
var sel = parseInt($( this ).val());
|
||||||
|
@ -437,5 +465,4 @@ $(document).ready(function () {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
display: "none",
|
display: "none",
|
||||||
}).appendTo("body");
|
}).appendTo("body");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -36,6 +36,9 @@
|
||||||
<link href="{{ url_for('static', filename='css/jquery-ui.min.css') }}" rel="stylesheet" type="text/css" />
|
<link href="{{ url_for('static', filename='css/jquery-ui.min.css') }}" rel="stylesheet" type="text/css" />
|
||||||
<script src="{{ url_for('static', filename='js/jquery-ui.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/jquery-ui.min.js') }}"></script>
|
||||||
|
|
||||||
|
<link href="{{ url_for('static', filename='css/vis.min.css') }}" rel="stylesheet" type="text/css" />
|
||||||
|
<script src="{{ url_for('static', filename='js/vis.min.js') }}"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -206,6 +209,17 @@ small {
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /.col-lg-12 -->
|
</div><!-- /.col-lg-12 -->
|
||||||
|
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="panel panel-default" style="">
|
||||||
|
<div class="panel-heading" style="font-weight: bold;">
|
||||||
|
<b>Timeline</b>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body" style="">
|
||||||
|
<div id="timeline" style="width:100%; height: 100%;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.col-lg-12 -->
|
||||||
|
|
||||||
</div><!-- /.row -->
|
</div><!-- /.row -->
|
||||||
|
|
||||||
</div> <!-- /.container-fluid -->
|
</div> <!-- /.container-fluid -->
|
||||||
|
@ -226,6 +240,7 @@ small {
|
||||||
var url_getTrendingTag = "{{ url_for('getTrendingTags') }}";
|
var url_getTrendingTag = "{{ url_for('getTrendingTags') }}";
|
||||||
var url_getTrendingSightings = "{{ url_for('getTrendingSightings') }}";
|
var url_getTrendingSightings = "{{ url_for('getTrendingSightings') }}";
|
||||||
var url_getTrendingDisc = "{{ url_for('getTrendingDisc') }}";
|
var url_getTrendingDisc = "{{ url_for('getTrendingDisc') }}";
|
||||||
|
var url_getGenericTrendingOvertime = "{{ url_for('getGenericTrendingOvertime') }}";
|
||||||
|
|
||||||
var url_getTypeaheadData = "{{ url_for('getTypeaheadData') }}";
|
var url_getTypeaheadData = "{{ url_for('getTypeaheadData') }}";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue