Skip to content

Commit

Permalink
Ychat update messages (#81)
Browse files Browse the repository at this point in the history
* Allow to update a message from the backend

* insert a new message from backend at the correct position regarding to the timestamp

* Add tests on the YChat

* Add a dedicated tests for pytest
  • Loading branch information
brichet authored Sep 13, 2024
1 parent f30fe91 commit 67dbc0d
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 24 deletions.
23 changes: 22 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,27 @@ jobs:
set -eux
jlpm run test
test_extensions:
runs-on: ubuntu-latest
needs: build_jupyter-chat
name: Python test on extensions

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

- name: Install dependencies
run: python -m pip install -U "jupyterlab>=4.0.0,<5"

- name: Build the extensions
run: |
set -eux
./scripts/install.sh
pytest -vv -r ap --cov
build_extensions:
runs-on: ubuntu-latest
needs: build_jupyter-chat
Expand All @@ -57,7 +78,7 @@ jobs:
- name: Install dependencies
run: python -m pip install -U "jupyterlab>=4.0.0,<5"

- name: Build package
- name: Package the extensions
run: |
jlpm install
jlpm build:${{ matrix.extension }}
Expand Down
1 change: 0 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

import pytest

pytest_plugins = ("pytest_jupyter.jupyter_server", )
11 changes: 0 additions & 11 deletions python/jupyterlab-collaborative-chat/conftest.py

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

# import jupyter_ydoc before YChat to avoid circular error
import jupyter_ydoc

import pytest
import time
from copy import deepcopy
from uuid import uuid4
from ..ychat import YChat

USER = {
"username": str(uuid4()),
"name": "Test user",
"display_name": "Test user"
}

USER2 = {
"username": str(uuid4()),
"name": "Test user 2",
"display_name": "Test user 2"
}


def create_message():
return {
"type": "msg",
"id": str(uuid4()),
"body": "This is a test message",
"time": time.time(),
"sender": USER["username"]
}


def test_initialize_ychat():
chat = YChat()
assert chat.get_messages() == []
assert chat.get_users() == {}
assert chat.get_metadata() == {}


def test_add_user():
chat = YChat()
chat.set_user(USER)
assert USER["username"] in chat.get_users().keys()
assert chat.get_users()[USER["username"]] == USER


def test_get_user_by_name():
chat = YChat()
chat.set_user(USER)
chat.set_user(USER2)
assert chat.get_user_by_name(USER["name"]) == USER
assert chat.get_user_by_name(USER2["name"]) == USER2
assert chat.get_user_by_name(str(uuid4())) == None


def test_add_message():
chat = YChat()
msg = create_message()
chat.add_message(msg)
assert len(chat.get_messages()) == 1
assert chat.get_messages()[0] == msg


def test_set_message_should_add():
chat = YChat()
msg = create_message()
chat.set_message(msg)
assert len(chat.get_messages()) == 1
assert chat.get_messages()[0] == msg


def test_set_message_should_update():
chat = YChat()
msg = create_message()
index = chat.add_message(msg)
msg["body"] = "Updated content"
chat.set_message(msg, index)
assert len(chat.get_messages()) == 1
assert chat.get_messages()[0] == msg


def test_set_message_should_add_with_new_id():
chat = YChat()
msg = create_message()
index = chat.add_message(msg)
new_msg = deepcopy(msg)
new_msg["id"] = str(uuid4())
new_msg["body"] = "Updated content"
chat.set_message(new_msg, index)
assert len(chat.get_messages()) == 2
assert chat.get_messages()[0] == msg
assert chat.get_messages()[1] == new_msg


def test_set_message_should_update_with_wrong_index():
chat = YChat()
msg = create_message()
chat.add_message(msg)
new_msg = create_message()
new_msg["body"] = "New content"
index = chat.add_message(new_msg)
assert index == 1
new_msg["body"] = "Updated content"
chat.set_message(new_msg, 0)
assert len(chat.get_messages()) == 2
assert chat.get_messages()[0] == msg
assert chat.get_messages()[1] == new_msg

Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ def set_user(self, user: dict[str, str]) -> None:
with self._ydoc.transaction():
self._yusers.update({user["username"]: user})

def get_message(self, id: str) -> dict | None:
def get_message(self, id: str) -> tuple[dict | None, int | None]:
"""
Returns a message from its id, or None
Returns a message and its index from its id, or None
"""
return next(
(msg for msg in self.get_messages() if msg["id"] == id),
None
((msg, i) for i, msg in enumerate(self.get_messages()) if msg["id"] == id),
(None, None)
)

def get_messages(self) -> list[dict]:
Expand All @@ -97,12 +97,45 @@ def get_messages(self) -> list[dict]:
"""
return self._ymessages.to_py()

def add_message(self, message: dict) -> None:
def add_message(self, message: dict) -> int:
"""
Append a message to the document.
"""
timestamp: float = time.time()
message["time"] = timestamp
with self._ydoc.transaction():
index = len(self._ymessages) - next((i for i, v in enumerate(self.get_messages()[::-1]) if v["time"] < timestamp), len(self._ymessages))
self._ymessages.insert(index, message)
return index

def update_message(self, message: dict, index: int, append: bool = False):
"""
Appends a message to the document.
Update a message of the document.
If append is True, the content will be append to the previous content.
"""
with self._ydoc.transaction():
self._ymessages.append(message)
initial_message = self._ymessages.pop(index)
if append:
message["body"] = initial_message["body"] + message["body"]
self._ymessages.insert(index, message)

def set_message(self, message: dict, index: int | None = None, append: bool = False):
"""
Update or append a message.
"""

if index is not None and 0 <= index < len(self._ymessages):
initial_message = self._ymessages[index]
else:
return self.add_message(message)

if not initial_message["id"] == message["id"]:
initial_message, index = self.get_message(message["id"])
if initial_message is None:
return self.add_message(message)

self.update_message(message, index, append)
return index

def get_single_metadata(self, name) -> dict:
"""
Expand Down

0 comments on commit 67dbc0d

Please sign in to comment.