from sqlalchemy.orm import Session from sqlalchemy.sql.expression import select import database as db class Message: def __init__(self, id, content, sender, time=None): self.id = id self.content = content self.sender = sender self.time = time def from_db_record(record): return Message(record.id, record.content, record.sender, record.time) def __str__(self): return f"Message(#{self.id} ({self.time if self.time != None else 'no-time'}) {self.sender}: {self.content.replace("\n", "\\n")})" class Thread: def __init__(self, id, chat_id, name=None, description=None): self.id = id self.chat_id = chat_id self.name = name self.description = description self.messages = [] class ChatView: def __init__(self, app, chat_id): self.app = app self.chat_id = chat_id self.name = None self.description = None # Thread holding messages without a set thread id self.default_thread = Thread(None, chat_id) # Most recent thread objects, earliest to latest self.threads = [] # Map of threads by their id self.thread_map = {} # Long term summary of this chat self.summary = None # Id of the thread currently in focus, by default the last thread appearing in the conversation self.current_thread_id = None self._earliest_message_id = None self._load_basic_data() self._load_next_messages(20) def _load_basic_data(self): with self.app.sql_session() as session: record = session.execute( select(db.Chat).where(db.Chat.id == self.chat_id) ).scalars().first() if not record: raise Exception(f"Chat not found with id {self.chat_id}") self.name = record.name self.description = record.description def _load_next_messages(self, load_count): with self.app.sql_session() as session: records = session.execute( select(db.Message).where( db.Message.chat_id == self.chat_id, self._earliest_message_id == None or db.Message.id <= self._earliest_message_id ).order_by(db.Message.time.asc()) ).scalars().fetchmany(load_count) print(f"Loaded messages. Threads: {bool(self.threads)}, records: {bool(records)}") if not self.threads and records: print(f"Last Record: ", records[-1]) self.current_thread_id = records[-1].thread_id # Go over messages from newest to oldest for record in reversed(records): if record.thread_id == None: thread = self.default_thread else: thread = self.thread_map.get(record.thread_id) if not thread: thread = Thread(record.thread_id, self.chat_id) self.thread_map[thread.id] = thread self.threads.insert(0, thread) # This is the oldest message so far, add it to the start of the chat message = Message(record.id, record.content, record.sender, record.time) thread.messages.insert(0, message) # Update the earliest id for the next load operation if records: self._earliest_message_id = records[0].id def get_thread(self, thread_id): if thread_id == None: return self.default_thread return self.thread_map.get(thread_id) def get_current_thread(self): # TODO: if !thread return self.get_thread(self.current_thread_id) def debug_string(self): result_lines = [] def _print(line): result_lines.append(line) def print_thread(thread, title=None): if title: _print(title + ":") elif thread.name: _print(thread.name + ":") else: _print(f"Thread {thread.id}:") for message in thread.messages: _print(" " + str(message)) print_thread(self.default_thread, "Default Thread") for thread in self.threads: print_thread(thread) _print("Current thread id: " + str(self.current_thread_id)) return "\n".join(result_lines)