Skip to content

Commit

Permalink
chg: [chat viewer] add chat messages by current year heatmap
Browse files Browse the repository at this point in the history
  • Loading branch information
Terrtia committed Dec 20, 2024
1 parent 55a35bf commit 9a388dc
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 103 deletions.
13 changes: 13 additions & 0 deletions bin/lib/chats_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,19 @@ def api_get_nb_week_messages(chat_type, chat_instance_uuid, chat_id):
week = chat.get_nb_week_messages()
return week, 200

def api_get_nb_year_messages(chat_type, chat_instance_uuid, chat_id, year):
chat = get_obj_chat(chat_type, chat_instance_uuid, chat_id)
if not chat.exists():
return {"status": "error", "reason": "Unknown chat"}, 404
try:
year = int(year)
except (TypeError, ValueError):
year = datetime.now().year
nb_max, nb = chat.get_nb_year_messages(year)
nb = [[date, value] for date, value in nb.items()]
return {'max': nb_max, 'nb': nb}, 200


def api_get_chat_participants(chat_type, chat_subtype, chat_id):
if chat_type not in ['chat', 'chat-subchannel', 'chat-thread']:
return {"status": "error", "reason": "Unknown chat type"}, 400
Expand Down
42 changes: 39 additions & 3 deletions bin/lib/objects/abstract_chat_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import time
from abc import ABC

from datetime import datetime
from datetime import datetime, timezone
# from flask import url_for

sys.path.append(os.environ['AIL_BIN'])
Expand Down Expand Up @@ -160,10 +160,14 @@ def _get_messages(self, nb=-1, page=-1):
return messages, {'nb': nb, 'page': page, 'nb_pages': nb_pages, 'total': total, 'nb_first': nb_first, 'nb_last': nb_last}

def get_timestamp_first_message(self):
return r_object.zrange(f'messages:{self.type}:{self.subtype}:{self.id}', 0, 0, withscores=True)
first = r_object.zrange(f'messages:{self.type}:{self.subtype}:{self.id}', 0, 0, withscores=True)
if first:
return int(first[0][1])

def get_timestamp_last_message(self):
return r_object.zrevrange(f'messages:{self.type}:{self.subtype}:{self.id}', 0, 0, withscores=True)
last = r_object.zrevrange(f'messages:{self.type}:{self.subtype}:{self.id}', 0, 0, withscores=True)
if last:
return int(last[0][1])

def get_first_message(self):
return r_object.zrange(f'messages:{self.type}:{self.subtype}:{self.id}', 0, 0)
Expand Down Expand Up @@ -223,6 +227,38 @@ def get_nb_week_messages(self):
nb_day += 1
return stats

def get_message_years(self):
timestamp = datetime.utcfromtimestamp(float(self.get_timestamp_first_message()))
year_start = int(timestamp.strftime('%Y'))
timestamp = datetime.utcfromtimestamp(float(self.get_timestamp_last_message()))
year_end = int(timestamp.strftime('%Y'))
return list(range(year_start, year_end + 1))

def get_nb_year_messages(self, year):
nb_year = {}
nb_max = 0
start = int(datetime(year, 1, 1, 0, 0, 0, tzinfo=timezone.utc).timestamp())
end = int(datetime(year, 12, 31, 23, 59, 59, tzinfo=timezone.utc).timestamp())

for mess_t in r_object.zrangebyscore(f'messages:{self.type}:{self.subtype}:{self.id}', start, end, withscores=True):
timestamp = datetime.utcfromtimestamp(float(mess_t[1]))
date = timestamp.strftime('%Y-%m-%d')
if date not in nb_year:
nb_year[date] = 0
nb_year[date] += 1
nb_max = max(nb_max, nb_year[date])

subchannels = self.get_subchannels()
for gid in subchannels:
for mess_t in r_object.zrangebyscore(f'messages:{gid}', start, end, withscores=True):
timestamp = datetime.utcfromtimestamp(float(mess_t[1]))
date = timestamp.strftime('%Y-%m-%d')
if date not in nb_year:
nb_year[date] = 0
nb_year[date] += 1

return nb_max, nb_year

def get_message_meta(self, message, timestamp=None, translation_target='', options=None): # TODO handle file message
message = Messages.Message(message[9:])
if not options:
Expand Down
20 changes: 20 additions & 0 deletions var/www/blueprints/chats_explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ def chats_explorer_instance():
chat_instance = chat_instance[0]
return render_template('chat_instance.html', chat_instance=chat_instance)

@chats_explorer.route("chats/explorer/chats/selector", methods=['GET'])
@login_required
@login_read_only
def chats_explorer_chats_selector():
return jsonify(chats_viewer.api_get_chats_selector())

@chats_explorer.route("chats/explorer/chat", methods=['GET'])
@login_required
@login_read_only
Expand Down Expand Up @@ -123,6 +129,20 @@ def chats_explorer_messages_stats_week_all():
else:
return jsonify(week[0])

@chats_explorer.route("chats/explorer/messages/stats/year", methods=['GET'])
@login_required
@login_read_only
def chats_explorer_messages_stats_year():
chat_type = request.args.get('type')
instance_uuid = request.args.get('subtype')
chat_id = request.args.get('id')
year = request.args.get('year')
stats = chats_viewer.api_get_nb_year_messages(chat_type, instance_uuid, chat_id, year)
if stats[1] != 200:
return create_json_response(stats[0], stats[1])
else:
return jsonify(stats[0])

@chats_explorer.route("/chats/explorer/subchannel", methods=['GET'])
@login_required
@login_read_only
Expand Down
151 changes: 51 additions & 100 deletions var/www/templates/chats_explorer/chat_viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>

<head>
<title>Chats Protocols - AIL</title>
<title>Chat - AIL</title>
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">

<!-- Core CSS -->
Expand All @@ -18,6 +18,7 @@
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/d3.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/d3/heatmap_week_hour.js')}}"></script>
<script src="{{ url_for('static', filename='js/echarts.min.js')}}"></script>

<style>
.chat-message-left,
Expand Down Expand Up @@ -112,6 +113,9 @@ <h5 class="mx-5 text-secondary">This week:</h5>
<div id="heatmapweekhour"></div>
{% endif %}

<h5>Messages by year:</h5>
<div id="heatmapyear" style="width: 100%;height: 300px;"></div>

{% with translate_url=url_for('chats_explorer.chats_explorer_chat', subtype=chat['subtype']), obj_id=chat['id'], pagination=chat['pagination'] %}
{% include 'chats_explorer/block_translation.html' %}
{% endwith %}
Expand Down Expand Up @@ -218,109 +222,56 @@ <h5 class="mx-5 text-secondary">This week:</h5>
})
{% endif %}

/*
// set the dimensions and margins of the graph
const margin = {top: 30, right: 30, bottom: 30, left: 30},
width = 450 - margin.left - margin.right,
height = 450 - margin.top - margin.bottom;
// append the svg object to the body of the page
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// Labels of row and columns
const myGroups = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
const myVars = ["v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10"]
//Read the data
d3.csv("").then( function(data) {
// Labels of row and columns -> unique identifier of the column called 'group' and 'variable'
const myGroups = Array.from(new Set(data.map(d => d.group)))
const myVars = Array.from(new Set(data.map(d => d.variable)))
// Build X scales and axis:
const x = d3.scaleBand()
.range([ 0, width ])
.domain(myGroups)
.padding(0.05);
svg.append("g")
.style("font-size", 15)
.attr("transform", `translate(0, ${height})`)
.call(d3.axisBottom(x).tickSize(0))
.select(".domain").remove()
// Build Y scales and axis:
const y = d3.scaleBand()
.range([ height, 0 ])
.domain(myVars)
.padding(0.01);
svg.append("g")
.style("font-size", 15)
.call(d3.axisLeft(y).tickSize(0))
.select(".domain").remove()
// Build color scale
const myColor = d3.scaleSequential()
.interpolator(d3.interpolateInferno)
.domain([1,100])
// create a tooltip
const tooltip = d3.select("#my_dataviz")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
// Three function that change the tooltip when user hover / move / leave a cell
const mouseover = function(event,d) {
tooltip.style("opacity", 1)
d3.select(this)
.style("stroke", "black")
.style("opacity", 1)
}
const mousemove = function(event,d) {
tooltip.html("The exact value of<br>this cell is: " + d)
.style("left", (event.x)/2 + "px")
.style("top", (event.y)/2 + "px")
}
const mouseleave = function(d) {
tooltip.style("opacity", 0)
d3.select(this)
.style("stroke", "none")
.style("opacity", 0.8)
}

var heatyearChart = echarts.init(document.getElementById('heatmapyear'));
window.addEventListener('resize', function() {
heatyearChart.resize();
});
var optionheatmap;

svg.selectAll()
.data(data, function(d) {return d.group+':'+d.variable;})
.join("rect")
.attr("x", function(d) { return x(d.group) })
.attr("y", function(d) { return y(d.variable) })
.attr("rx", 4)
.attr("ry", 4)
.attr("width", x.bandwidth() )
.attr("height", y.bandwidth() )
.style("fill", function(d) { return myColor(d.value)} )
.style("stroke-width", 4)
.style("stroke", "none")
.style("opacity", 0.8)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)
optionheatmap = {
tooltip: {
position: 'top',
formatter: function (p) {
//const format = echarts.time.format(p.data[0], '{yyyy}-{MM}-{dd}', false);
return p.data[0] + ': ' + p.data[1];
}
},
visualMap: {
min: 0,
max: 100,
calculable: true,
orient: 'horizontal',
left: '500',
top: '-10'
},
calendar: [
{
orient: 'horizontal',
//range: new Date().getFullYear(),
range: '2024',
},
],
series: [
{
type: 'heatmap',
coordinateSystem: 'calendar',
data: []
},

]
};
heatyearChart.setOption(optionheatmap);

$.getJSON("{{ url_for('chats_explorer.chats_explorer_messages_stats_year') }}?type=chat&subtype={{ chat['subtype'] }}&id={{ chat['id'] }}")
.done(function(data) {
optionheatmap['visualMap']['max'] = data['max']
optionheatmap['series'][0]['data'] = data['nb']
heatyearChart.setOption(optionheatmap)

})
}
);

*/
</script>


Expand Down

0 comments on commit 9a388dc

Please sign in to comment.