From 175a3b0911ff4b37fb1e3c558283df5f280422d5 Mon Sep 17 00:00:00 2001 From: James Meakin <12661555+jmsmkn@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:04:11 +0100 Subject: [PATCH] Add Direct Messages (#3093) This PR adds the Direct Messages application. It introduces three main new models: - `Conversation`: Many users are `participants` of one conversation. Many permissions are added to each conversation - view, create_conversation_direct_message, mark_conversation_read, mark_conversation_message_as_spam. - `DirectMessage`: Has a foreign key to a `conversation`, `sender` and which users have not read this message. The sender can delete the message and the conversation participants can mark the message as spam. If a direct message is created the other participants in the group have the message marked as unread. If a message is deleted or marked as spam then all of the unread markers are deleted. - `Mute`: tracks which users have muted which other users. If a direct message is created and the receiver has muted the sender the message is not marked as unread by the receiver. The views use HTMX. As a conversation is loaded all of the messages are marked as read as a callback. For now only challenge admins can message their participants. See https://github.com/DIAGNijmegen/rse-roadmap/issues/255 --- app/config/settings.py | 1 + app/config/urls/root.py | 6 + .../grandchallenge/partials/userlinks.html | 22 +- .../direct_messages/__init__.py | 0 app/grandchallenge/direct_messages/admin.py | 75 +++ app/grandchallenge/direct_messages/apps.py | 9 + app/grandchallenge/direct_messages/forms.py | 188 +++++++ .../migrations/0001_initial.py | 433 ++++++++++++++++ .../direct_messages/migrations/__init__.py | 0 app/grandchallenge/direct_messages/models.py | 242 +++++++++ app/grandchallenge/direct_messages/signals.py | 55 ++ .../direct_messages/direct_message_list.mjs | 50 ++ .../direct_messages/conversation_detail.html | 154 ++++++ .../direct_messages/conversation_form.html | 10 + .../direct_messages/conversation_list.html | 41 ++ .../direct_messages/directmessage_form.html | 10 + .../templates/direct_messages/mute_form.html | 10 + .../partials/conversation_select_detail.html | 45 ++ .../partials/directmessage_detail.html | 15 + app/grandchallenge/direct_messages/urls.py | 64 +++ app/grandchallenge/direct_messages/views.py | 301 +++++++++++ .../evaluation/evaluation_detail.html | 9 + .../evaluation/views/__init__.py | 22 + .../profiles/templatetags/profiles.py | 12 +- app/grandchallenge/verifications/models.py | 11 + app/tests/direct_messages_tests/__init__.py | 0 app/tests/direct_messages_tests/factories.py | 29 ++ .../direct_messages_tests/test_models.py | 176 +++++++ .../direct_messages_tests/test_signals.py | 93 ++++ app/tests/direct_messages_tests/test_views.py | 490 ++++++++++++++++++ scripts/development_fixtures.py | 34 +- 31 files changed, 2590 insertions(+), 17 deletions(-) create mode 100644 app/grandchallenge/direct_messages/__init__.py create mode 100644 app/grandchallenge/direct_messages/admin.py create mode 100644 app/grandchallenge/direct_messages/apps.py create mode 100644 app/grandchallenge/direct_messages/forms.py create mode 100644 app/grandchallenge/direct_messages/migrations/0001_initial.py create mode 100644 app/grandchallenge/direct_messages/migrations/__init__.py create mode 100644 app/grandchallenge/direct_messages/models.py create mode 100644 app/grandchallenge/direct_messages/signals.py create mode 100644 app/grandchallenge/direct_messages/static/js/direct_messages/direct_message_list.mjs create mode 100644 app/grandchallenge/direct_messages/templates/direct_messages/conversation_detail.html create mode 100644 app/grandchallenge/direct_messages/templates/direct_messages/conversation_form.html create mode 100644 app/grandchallenge/direct_messages/templates/direct_messages/conversation_list.html create mode 100644 app/grandchallenge/direct_messages/templates/direct_messages/directmessage_form.html create mode 100644 app/grandchallenge/direct_messages/templates/direct_messages/mute_form.html create mode 100644 app/grandchallenge/direct_messages/templates/direct_messages/partials/conversation_select_detail.html create mode 100644 app/grandchallenge/direct_messages/templates/direct_messages/partials/directmessage_detail.html create mode 100644 app/grandchallenge/direct_messages/urls.py create mode 100644 app/grandchallenge/direct_messages/views.py create mode 100644 app/tests/direct_messages_tests/__init__.py create mode 100644 app/tests/direct_messages_tests/factories.py create mode 100644 app/tests/direct_messages_tests/test_models.py create mode 100644 app/tests/direct_messages_tests/test_signals.py create mode 100644 app/tests/direct_messages_tests/test_views.py diff --git a/app/config/settings.py b/app/config/settings.py index 1356fcd903..3a6532ab30 100644 --- a/app/config/settings.py +++ b/app/config/settings.py @@ -598,6 +598,7 @@ "grandchallenge.charts", "grandchallenge.forums", "grandchallenge.invoices", + "grandchallenge.direct_messages", ] INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS + THIRD_PARTY_APPS diff --git a/app/config/urls/root.py b/app/config/urls/root.py index f00478df40..eb4d34cbad 100644 --- a/app/config/urls/root.py +++ b/app/config/urls/root.py @@ -175,6 +175,12 @@ def handler500(request): "components/", include("grandchallenge.components.urls", namespace="components"), ), + path( + "messages/", + include( + "grandchallenge.direct_messages.urls", namespace="direct-messages" + ), + ), ] if settings.DEBUG and settings.ENABLE_DEBUG_TOOLBAR: diff --git a/app/grandchallenge/core/templates/grandchallenge/partials/userlinks.html b/app/grandchallenge/core/templates/grandchallenge/partials/userlinks.html index 732d15358d..a73bee03b8 100644 --- a/app/grandchallenge/core/templates/grandchallenge/partials/userlinks.html +++ b/app/grandchallenge/core/templates/grandchallenge/partials/userlinks.html @@ -42,19 +42,31 @@ {% if user.is_authenticated %} - {% if ACTSTREAM_ENABLE %} + {% if ACTSTREAM_ENABLE %} + {% endif %}