diff --git a/bin/lib/chats_viewer.py b/bin/lib/chats_viewer.py index f46d852a..49916d4e 100755 --- a/bin/lib/chats_viewer.py +++ b/bin/lib/chats_viewer.py @@ -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 diff --git a/bin/lib/objects/abstract_chat_object.py b/bin/lib/objects/abstract_chat_object.py index b4ba87ad..3987cc3a 100755 --- a/bin/lib/objects/abstract_chat_object.py +++ b/bin/lib/objects/abstract_chat_object.py @@ -11,7 +11,7 @@ import sys 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']) @@ -160,10 +160,14 @@ class AbstractChatObject(AbstractSubtypeObject, ABC): 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) @@ -223,6 +227,38 @@ class AbstractChatObject(AbstractSubtypeObject, ABC): 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: diff --git a/var/www/blueprints/chats_explorer.py b/var/www/blueprints/chats_explorer.py index 1552ae62..bb4d0cbd 100644 --- a/var/www/blueprints/chats_explorer.py +++ b/var/www/blueprints/chats_explorer.py @@ -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 @@ -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 diff --git a/var/www/templates/chats_explorer/chat_viewer.html b/var/www/templates/chats_explorer/chat_viewer.html index 872f3b55..0d43fef8 100644 --- a/var/www/templates/chats_explorer/chat_viewer.html +++ b/var/www/templates/chats_explorer/chat_viewer.html @@ -2,7 +2,7 @@ - Chats Protocols - AIL + Chat - AIL @@ -18,6 +18,7 @@ +