chg: [chat explorer] show message, open message in chat/subchannel/thread
Some checks are pending
CI / ail_test (3.9) (push) Waiting to run
CI / ail_test (3.10) (push) Waiting to run
CI / ail_test (3.7) (push) Waiting to run
CI / ail_test (3.8) (push) Waiting to run

This commit is contained in:
terrtia 2025-01-13 15:25:04 +01:00
parent afd0d6b7d8
commit aecf71d5a3
No known key found for this signature in database
GPG key ID: 1E1B1F50D84613D0
11 changed files with 96 additions and 31 deletions

View file

@ -786,7 +786,7 @@ def api_get_chats_selector():
selector.append({'id': chat.get_global_id(), 'name': f'{chat.get_chat_instance()}: {chat.get_label()}'}) selector.append({'id': chat.get_global_id(), 'name': f'{chat.get_chat_instance()}: {chat.get_label()}'})
return selector return selector
def api_get_chat(chat_id, chat_instance_uuid, translation_target=None, nb=-1, page=-1, messages=True, heatmap=False): def api_get_chat(chat_id, chat_instance_uuid, translation_target=None, nb=-1, page=-1, messages=True, message=None, heatmap=False):
chat = Chats.Chat(chat_id, chat_instance_uuid) chat = Chats.Chat(chat_id, chat_instance_uuid)
if not chat.exists(): if not chat.exists():
return {"status": "error", "reason": "Unknown chat"}, 404 return {"status": "error", "reason": "Unknown chat"}, 404
@ -800,7 +800,7 @@ def api_get_chat(chat_id, chat_instance_uuid, translation_target=None, nb=-1, pa
if translation_target not in Language.get_translation_languages(): if translation_target not in Language.get_translation_languages():
translation_target = None translation_target = None
if messages: if messages:
meta['messages'], meta['pagination'], meta['tags_messages'] = chat.get_messages(translation_target=translation_target, nb=nb, page=page) meta['messages'], meta['pagination'], meta['tags_messages'] = chat.get_messages(translation_target=translation_target, message=message, nb=nb, page=page)
meta['messages'] = get_chat_object_messages_meta(meta['messages']) meta['messages'] = get_chat_object_messages_meta(meta['messages'])
if heatmap: if heatmap:
meta['years'] = chat.get_message_years() meta['years'] = chat.get_message_years()
@ -849,7 +849,7 @@ def api_get_chat_participants(chat_type, chat_subtype, chat_id):
meta['participants'] = chat_participants meta['participants'] = chat_participants
return meta, 200 return meta, 200
def api_get_subchannel(chat_id, chat_instance_uuid, translation_target=None, nb=-1, page=-1): def api_get_subchannel(chat_id, chat_instance_uuid, translation_target=None, message=None, nb=-1, page=-1):
subchannel = ChatSubChannels.ChatSubChannel(chat_id, chat_instance_uuid) subchannel = ChatSubChannels.ChatSubChannel(chat_id, chat_instance_uuid)
if not subchannel.exists(): if not subchannel.exists():
return {"status": "error", "reason": "Unknown subchannel"}, 404 return {"status": "error", "reason": "Unknown subchannel"}, 404
@ -861,11 +861,11 @@ def api_get_subchannel(chat_id, chat_instance_uuid, translation_target=None, nb=
meta['threads'] = get_threads_metas(meta['threads']) meta['threads'] = get_threads_metas(meta['threads'])
if meta.get('username'): if meta.get('username'):
meta['username'] = get_username_meta_from_global_id(meta['username']) meta['username'] = get_username_meta_from_global_id(meta['username'])
meta['messages'], meta['pagination'], meta['tags_messages'] = subchannel.get_messages(translation_target=translation_target, nb=nb, page=page) meta['messages'], meta['pagination'], meta['tags_messages'] = subchannel.get_messages(translation_target=translation_target, message=message, nb=nb, page=page)
meta['messages'] = get_chat_object_messages_meta(meta['messages']) meta['messages'] = get_chat_object_messages_meta(meta['messages'])
return meta, 200 return meta, 200
def api_get_thread(thread_id, thread_instance_uuid, translation_target=None, nb=-1, page=-1): def api_get_thread(thread_id, thread_instance_uuid, translation_target=None, message=None, nb=-1, page=-1):
thread = ChatThreads.ChatThread(thread_id, thread_instance_uuid) thread = ChatThreads.ChatThread(thread_id, thread_instance_uuid)
if not thread.exists(): if not thread.exists():
return {"status": "error", "reason": "Unknown thread"}, 404 return {"status": "error", "reason": "Unknown thread"}, 404
@ -873,7 +873,7 @@ def api_get_thread(thread_id, thread_instance_uuid, translation_target=None, nb=
meta = thread.get_meta({'chat', 'nb_messages', 'nb_participants'}) meta = thread.get_meta({'chat', 'nb_messages', 'nb_participants'})
# if meta['chat']: # if meta['chat']:
# meta['chat'] = get_chat_meta_from_global_id(meta['chat']) # meta['chat'] = get_chat_meta_from_global_id(meta['chat'])
meta['messages'], meta['pagination'], meta['tags_messages'] = thread.get_messages(translation_target=translation_target, nb=nb, page=page) meta['messages'], meta['pagination'], meta['tags_messages'] = thread.get_messages(translation_target=translation_target, message=message, nb=nb, page=page)
meta['messages'] = get_chat_object_messages_meta(meta['messages']) meta['messages'] = get_chat_object_messages_meta(meta['messages'])
return meta, 200 return meta, 200
@ -881,7 +881,7 @@ def api_get_message(message_id, translation_target=None):
message = Messages.Message(message_id) message = Messages.Message(message_id)
if not message.exists(): if not message.exists():
return {"status": "error", "reason": "Unknown uuid"}, 404 return {"status": "error", "reason": "Unknown uuid"}, 404
meta = message.get_meta({'barcodes', 'chat', 'content', 'files-names', 'forwarded_from', 'icon', 'images', 'language', 'link', 'parent', 'parent_meta', 'qrcodes', 'reactions', 'thread', 'translation', 'user-account'}, translation_target=translation_target) meta = message.get_meta({'barcodes', 'chat', 'container', 'content', 'files-names', 'forwarded_from', 'icon', 'images', 'language', 'link', 'parent', 'parent_meta', 'qrcodes', 'reactions', 'thread', 'translation', 'user-account'}, translation_target=translation_target)
if 'forwarded_from' in meta: if 'forwarded_from' in meta:
chat = get_obj_chat_from_global_id(meta['forwarded_from']) chat = get_obj_chat_from_global_id(meta['forwarded_from'])
meta['forwarded_from'] = chat.get_meta({'icon'}) meta['forwarded_from'] = chat.get_meta({'icon'})

View file

@ -55,9 +55,9 @@ class ChatSubChannel(AbstractChatObject):
def get_link(self, flask_context=False): def get_link(self, flask_context=False):
if flask_context: if flask_context:
url = url_for('correlation.show_correlation', type=self.type, subtype=self.subtype, id=self.id) url = url_for('chats_explorer.objects_subchannel_messages', subtype=self.subtype, id=self.id)
else: else:
url = f'{baseurl}/correlation/show?type={self.type}&subtype={self.subtype}&id={self.id}' url = f'{baseurl}/chats/explorer/subchannel?subtype={self.subtype}&id={self.id}'
return url return url
def get_svg_icon(self): # TODO def get_svg_icon(self): # TODO

View file

@ -49,9 +49,9 @@ class ChatThread(AbstractChatObject):
def get_link(self, flask_context=False): def get_link(self, flask_context=False):
if flask_context: if flask_context:
url = url_for('correlation.show_correlation', type=self.type, subtype=self.subtype, id=self.id) url = url_for('chats_explorer.objects_thread_messages', subtype=self.subtype, id=self.id)
else: else:
url = f'{baseurl}/correlation/show?type={self.type}&subtype={self.subtype}&id={self.id}' url = f'{baseurl}/chats/explorer/thread?subtype={self.subtype}&id={self.id}'
return url return url
def get_svg_icon(self): # TODO def get_svg_icon(self): # TODO

View file

@ -50,7 +50,7 @@ class Chat(AbstractChatObject):
if flask_context: if flask_context:
url = url_for('chats_explorer.chats_explorer_chat', subtype=self.subtype, id=self.id) url = url_for('chats_explorer.chats_explorer_chat', subtype=self.subtype, id=self.id)
else: else:
url = f'{baseurl}/correlation/show?type={self.type}&subtype={self.subtype}&id={self.id}' url = f'{baseurl}/chats/explorer/chat?subtype={self.subtype}&id={self.id}'
return url return url
def get_origin_link(self): def get_origin_link(self):

View file

@ -265,6 +265,8 @@ class Message(AbstractObject):
if options is None: if options is None:
options = set() options = set()
meta = self.get_default_meta(tags=True) meta = self.get_default_meta(tags=True)
# original_id
meta['_id'] = self.id.rsplit('/', 1)[-1]
# timestamp # timestamp
if not timestamp: if not timestamp:
@ -304,6 +306,8 @@ class Message(AbstractObject):
meta['user-account'] = self.get_user_account(meta=True) meta['user-account'] = self.get_user_account(meta=True)
if not meta['user-account']: if not meta['user-account']:
meta['user-account'] = {'id': 'UNKNOWN'} meta['user-account'] = {'id': 'UNKNOWN'}
if 'container' in options:
meta['container'] = self.get_container()
if 'chat' in options: if 'chat' in options:
meta['chat'] = self.get_chat_id() meta['chat'] = self.get_chat_id()
if 'thread' in options: if 'thread' in options:
@ -345,7 +349,16 @@ class Message(AbstractObject):
## Language ## ## Language ##
def get_root_obj(self): def get_root_obj(self):
return self.get_objs_container(root=True) return self.get_objs_container(root=True).pop()
def get_container(self):
thread = self.get_current_thread()
if thread:
return thread
subchannel = self.get_subchannel()
if subchannel:
return subchannel
return self.get_chat()
def get_objs_container(self, root=False): def get_objs_container(self, root=False):
objs_containers = set() objs_containers = set()

View file

@ -128,6 +128,12 @@ class AbstractChatObject(AbstractSubtypeObject, ABC):
def get_nb_messages(self): def get_nb_messages(self):
return r_object.zcard(f'messages:{self.type}:{self.subtype}:{self.id}') return r_object.zcard(f'messages:{self.type}:{self.subtype}:{self.id}')
def get_message_page(self, message, nb):
rank = r_object.zrank(f'messages:{self.type}:{self.subtype}:{self.id}', f'message::{message}')
if not rank:
return -1
return int(rank/ nb) + 1
def _get_messages(self, nb=-1, page=-1): def _get_messages(self, nb=-1, page=-1):
if nb < 1: if nb < 1:
messages = r_object.zrange(f'messages:{self.type}:{self.subtype}:{self.id}', 0, -1, withscores=True) messages = r_object.zrange(f'messages:{self.type}:{self.subtype}:{self.id}', 0, -1, withscores=True)
@ -273,7 +279,7 @@ class AbstractChatObject(AbstractSubtypeObject, ABC):
meta = message.get_meta(options=options, timestamp=timestamp, translation_target=translation_target) meta = message.get_meta(options=options, timestamp=timestamp, translation_target=translation_target)
return meta return meta
def get_messages(self, start=0, page=-1, nb=500, unread=False, options=None, translation_target='en'): # threads ???? # TODO ADD last/first message timestamp + return page def get_messages(self, start=0, page=-1, nb=500, message=None, unread=False, options=None, translation_target='en'): # threads ???? # TODO ADD last/first message timestamp + return page
# TODO return message meta # TODO return message meta
tags = {} tags = {}
messages = {} messages = {}
@ -282,12 +288,15 @@ class AbstractChatObject(AbstractSubtypeObject, ABC):
nb = int(nb) nb = int(nb)
except TypeError: except TypeError:
nb = 500 nb = 500
if not page: if message:
page = -1 page = self.get_message_page(message, nb)
try: else:
page = int(page) if not page:
except TypeError: page = -1
page = 1 try:
page = int(page)
except TypeError:
page = 1
mess, pagination = self._get_messages(nb=nb, page=page) mess, pagination = self._get_messages(nb=nb, page=page)
for message in mess: for message in mess:
timestamp = message[1] timestamp = message[1]

View file

@ -24,6 +24,7 @@ from lib import chats_viewer
from lib import Language from lib import Language
from lib import Tag from lib import Tag
from lib import module_extractor from lib import module_extractor
from lib.objects import ail_objects
# ============ BLUEPRINT ============ # ============ BLUEPRINT ============
chats_explorer = Blueprint('chats_explorer', __name__, template_folder=os.path.join(os.environ['AIL_FLASK'], 'templates/chats_explorer')) chats_explorer = Blueprint('chats_explorer', __name__, template_folder=os.path.join(os.environ['AIL_FLASK'], 'templates/chats_explorer'))
@ -93,8 +94,16 @@ def chats_explorer_chat():
if target == "Don't Translate": if target == "Don't Translate":
target = None target = None
nb_messages = request.args.get('nb') nb_messages = request.args.get('nb')
page = request.args.get('page') mess = request.args.get('message')
chat = chats_viewer.api_get_chat(chat_id, instance_uuid, translation_target=target, nb=nb_messages, page=page, heatmap=True) if mess:
message = mess
page = -1
message_id = message.rsplit('/', 1)[-1]
else:
message = None
message_id = None
page = request.args.get('page')
chat = chats_viewer.api_get_chat(chat_id, instance_uuid, translation_target=target, message=message, nb=nb_messages, page=page, heatmap=True)
if chat[1] != 200: if chat[1] != 200:
return create_json_response(chat[0], chat[1]) return create_json_response(chat[0], chat[1])
else: else:
@ -102,6 +111,7 @@ def chats_explorer_chat():
languages = Language.get_translation_languages() languages = Language.get_translation_languages()
return render_template('chat_viewer.html', chat=chat, bootstrap_label=bootstrap_label, return render_template('chat_viewer.html', chat=chat, bootstrap_label=bootstrap_label,
ail_tags=Tag.get_modal_add_tags(chat['id'], chat['type'], chat['subtype']), ail_tags=Tag.get_modal_add_tags(chat['id'], chat['type'], chat['subtype']),
message_id=message_id,
translation_languages=languages, translation_target=target) translation_languages=languages, translation_target=target)
@chats_explorer.route("chats/explorer/messages/stats/week", methods=['GET']) @chats_explorer.route("chats/explorer/messages/stats/week", methods=['GET'])
@ -154,8 +164,16 @@ def objects_subchannel_messages():
if target == "Don't Translate": if target == "Don't Translate":
target = None target = None
nb_messages = request.args.get('nb') nb_messages = request.args.get('nb')
page = request.args.get('page') mess = request.args.get('message')
subchannel = chats_viewer.api_get_subchannel(subchannel_id, instance_uuid, translation_target=target, nb=nb_messages, page=page) if mess:
message = mess
page = -1
message_id = message.rsplit('/', 1)[-1]
else:
message = None
message_id = None
page = request.args.get('page')
subchannel = chats_viewer.api_get_subchannel(subchannel_id, instance_uuid, translation_target=target, message=message, nb=nb_messages, page=page)
if subchannel[1] != 200: if subchannel[1] != 200:
return create_json_response(subchannel[0], subchannel[1]) return create_json_response(subchannel[0], subchannel[1])
else: else:
@ -163,6 +181,7 @@ def objects_subchannel_messages():
languages = Language.get_translation_languages() languages = Language.get_translation_languages()
return render_template('SubChannelMessages.html', subchannel=subchannel, return render_template('SubChannelMessages.html', subchannel=subchannel,
ail_tags=Tag.get_modal_add_tags(subchannel['id'], subchannel['type'], subchannel['subtype']), ail_tags=Tag.get_modal_add_tags(subchannel['id'], subchannel['type'], subchannel['subtype']),
message_id=message_id,
bootstrap_label=bootstrap_label, translation_languages=languages, translation_target=target) bootstrap_label=bootstrap_label, translation_languages=languages, translation_target=target)
@chats_explorer.route("/chats/explorer/thread", methods=['GET']) @chats_explorer.route("/chats/explorer/thread", methods=['GET'])
@ -175,14 +194,24 @@ def objects_thread_messages():
if target == "Don't Translate": if target == "Don't Translate":
target = None target = None
nb_messages = request.args.get('nb') nb_messages = request.args.get('nb')
page = request.args.get('page') mess = request.args.get('message')
thread = chats_viewer.api_get_thread(thread_id, instance_uuid, translation_target=target, nb=nb_messages, page=page) if mess:
message = mess
page = -1
message_id = message.rsplit('/', 1)[-1]
else:
message = None
message_id = None
page = request.args.get('page')
thread = chats_viewer.api_get_thread(thread_id, instance_uuid, translation_target=target, message=message, nb=nb_messages, page=page)
if thread[1] != 200: if thread[1] != 200:
return create_json_response(thread[0], thread[1]) return create_json_response(thread[0], thread[1])
else: else:
meta = thread[0] meta = thread[0]
languages = Language.get_translation_languages() languages = Language.get_translation_languages()
return render_template('ThreadMessages.html', meta=meta, bootstrap_label=bootstrap_label, translation_languages=languages, translation_target=target) return render_template('ThreadMessages.html', meta=meta, bootstrap_label=bootstrap_label,
message_id=message_id,
translation_languages=languages, translation_target=target)
@chats_explorer.route("/chats/explorer/participants", methods=['GET']) @chats_explorer.route("/chats/explorer/participants", methods=['GET'])
@login_required @login_required
@ -286,12 +315,13 @@ def objects_message():
else: else:
message = message[0] message = message[0]
languages = Language.get_translation_languages() languages = Language.get_translation_languages()
container_url = ail_objects.get_obj_from_global_id(message['container']).get_link(flask_context=True)
extracted = module_extractor.extract(current_user.get_user_id(), 'message', '', message['id'], content=message['content']) extracted = module_extractor.extract(current_user.get_user_id(), 'message', '', message['id'], content=message['content'])
extracted_matches = module_extractor.get_extracted_by_match(extracted) extracted_matches = module_extractor.get_extracted_by_match(extracted)
message['extracted'] = extracted message['extracted'] = extracted
message['extracted_matches'] = extracted_matches message['extracted_matches'] = extracted_matches
return render_template('ChatMessage.html', meta=message, bootstrap_label=bootstrap_label, return render_template('ChatMessage.html', meta=message, bootstrap_label=bootstrap_label,
translation_languages=languages, translation_target=target, translation_languages=languages, translation_target=target, container_url=container_url,
modal_add_tags=Tag.get_modal_add_tags(message['id'], object_type='message')) modal_add_tags=Tag.get_modal_add_tags(message['id'], object_type='message'))
@chats_explorer.route("/objects/message/translate", methods=['POST']) @chats_explorer.route("/objects/message/translate", methods=['POST'])

View file

@ -179,6 +179,9 @@
"order": [[ 3, "desc" ]] "order": [[ 3, "desc" ]]
}); });
{% endif %} {% endif %}
{% if message_id %}
document.location.hash = '#{{ message_id }}';
{% endif %}
}); });
function toggle_sidebar(){ function toggle_sidebar(){

View file

@ -196,7 +196,9 @@
$(document).ready(function(){ $(document).ready(function(){
$("#page-Decoded").addClass("active"); $("#page-Decoded").addClass("active");
$("#nav_chat").addClass("active"); $("#nav_chat").addClass("active");
{% if message_id %}
document.location.hash = '#{{ message_id }}';
{% endif %}
}); });
function toggle_sidebar(){ function toggle_sidebar(){

View file

@ -22,7 +22,7 @@
} }
</style> </style>
<div class="chat-message-left pb-1"> <div class="chat-message-left pb-1" id="{{ message['_id'] }}">
<div> <div>
<a href="{{ url_for('chats_explorer.objects_user_account')}}?subtype={{ message['user-account']['subtype'] }}&id={{ message['user-account']['id'] }}"> <a href="{{ url_for('chats_explorer.objects_user_account')}}?subtype={{ message['user-account']['subtype'] }}&id={{ message['user-account']['id'] }}">
<img src="{% if message['user-account']['icon'] %}{{ url_for('objects_image.image', filename=message['user-account']['icon'])}}{% else %}{{ url_for('static', filename='image/ail-icon.png') }}{% endif %}" <img src="{% if message['user-account']['icon'] %}{{ url_for('objects_image.image', filename=message['user-account']['icon'])}}{% else %}{{ url_for('static', filename='image/ail-icon.png') }}{% endif %}"
@ -199,7 +199,12 @@
</div> </div>
<a class="btn btn-light btn-sm text-secondary px-1" href="{{ url_for('correlation.show_correlation')}}?type={{ message['type'] }}&subtype={{ message['subtype'] }}&id={{ message['id'] }}"><i class="fas fa-project-diagram"></i></a> <a class="btn btn-light btn-sm text-secondary px-1" href="{{ url_for('correlation.show_correlation')}}?type={{ message['type'] }}&subtype={{ message['subtype'] }}&id={{ message['id'] }}"><i class="fas fa-project-diagram"></i></a>
<a class="btn btn-light btn-sm text-secondary px-1" href="{{ message['link'] }}"><i class="fas fa-eye"></i></a> {% if container_url %}
<a class="btn btn-light btn-sm text-secondary px-1" href="{{ container_url }}&message={{ message['id'] }}"><i class="fas fa-eye"></i></a>
{% else %}
<a class="btn btn-light btn-sm text-secondary px-1" href="{{ message['link'] }}"><i class="fas fa-eye"></i></a>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View file

@ -201,6 +201,9 @@
"order": [[ 5, "desc" ]] "order": [[ 5, "desc" ]]
}); });
{% endif %} {% endif %}
{% if message_id %}
document.location.hash = '#{{ message_id }}';
{% endif %}
}); });
function toggle_sidebar(){ function toggle_sidebar(){