Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions openedx/features/wikimedia_features/messenger/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Admin registration for Messenger.
"""
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _

from openedx.features.wikimedia_features.messenger.models import (
Message, Inbox
)


class MessageAdmin(admin.ModelAdmin):
"""
Admin config clearesult credit providers.
"""
search_fields = ('sender__username', 'receiver__username')
list_display = ('id', 'sender', 'receiver', 'message', 'created')


class InboxAdmin(admin.ModelAdmin):
"""
Admin config for clearesult credits offered by the courses.
"""
search_fields = ('sender', 'receiver')
list_display = ('id', 'sender', 'receiver', 'message', 'unread_count')

def sender(self, obj):
return obj.last_message.sender.username

def receiver(self, obj):
return obj.last_message.receiver.username

def message(self, obj):
return obj.last_message.message[:20]


admin.site.register(Message, MessageAdmin)
admin.site.register(Inbox, InboxAdmin)
Empty file.
Empty file.
109 changes: 109 additions & 0 deletions openedx/features/wikimedia_features/messenger/api/v0/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
Serializers for Messenger v0 API(s)
"""
from django.contrib.auth.models import User
from django.db import transaction
from django.utils.translation import ugettext as _
from rest_framework import serializers
from openedx.features.wikimedia_features.messenger.models import Inbox, Message
from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_urls_for_user


def validate_username(username):
try:
return User.objects.get(username=username)
except User.DoesNotExist:
raise serializers.ValidationError(_('User does not exist - invalid username {}'.format(username)))


class StringListField(serializers.ListField):
child = serializers.CharField()


class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username',)

class BulkMessageSerializer(serializers.Serializer):
receivers = StringListField()
message = serializers.CharField()

def validate_receivers(self, receivers):
if not receivers:
raise serializers.ValidationError(_('receiver list can not empty.'))
users = [validate_username(username) for username in receivers]
return users

def bulk_create(self, request=None):
created_messages = []
if not request:
raise serializers.ValidationError(_('Missing request object.'))
receivers = self.validated_data.get('receivers')
message = self.validated_data.get('message')
with transaction.atomic():
for user in receivers:
created_messages.append(
Message.objects.create(sender=request.user, receiver=user, message=message)
)
return created_messages


class InboxSerializer(serializers.ModelSerializer):
with_user = serializers.SerializerMethodField()
request = None

class Meta:
model = Inbox
fields = ('id', 'with_user', 'last_message', 'unread_count')

def __init__(self, *args, **kwargs):
super(InboxSerializer, self).__init__(*args, **kwargs)
self.request = self.context.get('request')

def get_with_user(self, obj):
if self.request:
if obj.last_message.sender != self.request.user:
return obj.last_message.sender.username
return obj.last_message.receiver.username
raise serializers.ValidationError(
_('Invalid request - request object not found.')
)

def to_representation(self, instance):
response = super().to_representation(instance)
with_user = User.objects.get(username=response.get('with_user'))
response['with_user_img'] = get_profile_image_urls_for_user(with_user, self.request).get('medium')
response['last_message'] =instance.last_message.message

# if last message is send by login-user then unread count will be 0
if self.request and instance.last_message.sender == self.request.user:
response['unread_count'] = 0

return response


class MessageSerializer(serializers.ModelSerializer):
receiver = serializers.CharField(source='receiver.username')
class Meta:
model = Message
fields = ('id', 'sender', 'receiver', 'message', 'created')
read_only_fields = ('id', 'created', 'sender')

def validate_receiver(self, receiver):
return validate_username(receiver)

def create(self, validated_data):
request = self.context.get('request')
validated_data['sender'] = request.user
validated_data['receiver'] = validated_data.get('receiver', {}).get('username')
return super().create(validated_data)

def to_representation(self, instance):
response = super().to_representation(instance)
response['sender'] = instance.sender.username
response['created'] = instance.created.strftime('%x-%I:%M %p')
response['sender_img']= get_profile_image_urls_for_user(
instance.sender, self.context.get('request')
).get('medium')
return response
55 changes: 55 additions & 0 deletions openedx/features/wikimedia_features/messenger/api/v0/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
Urls for Messenger v0 API(s)
"""
from django.conf.urls import url

from openedx.features.wikimedia_features.messenger.api.v0.views import (
InboxView, ConversationView, MessageCreateView, UserSearchView, BulkMessageView
)

app_name = 'messenger_api_v0'


urlpatterns = [
url(
r'^bulk_message/$',
BulkMessageView.as_view({
'post': 'bulk_message'
}),
name="bulk_message"
),
url(
r'^user/$',
UserSearchView.as_view({
'get': 'list'
}),
name="user_search"
),
url(
r'^inbox/$',
InboxView.as_view({
'get': 'list'
}),
name="user_inbox_list"
),
url(
r'^inbox/(?P<pk>\d+)/$',
InboxView.as_view({
'patch': 'partial_update',
'get': 'retrieve'
}),
name="user_inbox_detail"
),
url(
r'^conversation/$',
ConversationView.as_view({
'get': 'list',
}),
name="conversation_list"
),
url(
r'^message/$',
MessageCreateView.as_view(),
name="message_create"
),
]
190 changes: 190 additions & 0 deletions openedx/features/wikimedia_features/messenger/api/v0/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
"""
Views for Messenger v0 API(s)
"""
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from rest_framework import generics
from rest_framework import permissions
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication
from rest_framework.exceptions import NotFound, status
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response

from openedx.features.wikimedia_features.messenger.models import Inbox, Message
from openedx.features.wikimedia_features.messenger.api.v0.serializers import (
InboxSerializer, MessageSerializer, UserSerializer, BulkMessageSerializer
)


class MesssengerResultsSetPagination(PageNumberPagination):
page_size = 15

def get_paginated_response(self, data):
response = super(MesssengerResultsSetPagination, self).get_paginated_response(data)
response.data['num_pages'] = self.page.paginator.num_pages
response.data['count']: self.page.paginator.count
return response


class UserSearchView(viewsets.ReadOnlyModelViewSet):
"""
Search user's username containing given query string.

GET /messenger/api/v0/user/?search=name
Return list of users
[
{
"username": "username1"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this docstring needs to be updated now

},
...
]
"""
serializer_class = UserSerializer
authentication_classes = (SessionAuthentication,)
permission_classes = (permissions.IsAuthenticated,)

def get_queryset(self):
query = self.request.GET.get('search', '')
if query:
return User.objects.filter(
username__icontains=query
).exclude(username=self.request.user.username)
raise NotFound(_('Search query param is required.'))


class InboxView(viewsets.ModelViewSet):
"""
Returns list of all inbox messages of request user
Get /messenger/api/v0/inbox/
{
"count": 2,
"next": null,
"previous": null,
"num_pages": 1
"results": [
{
"id": 37,
"with_user": "staff",
"last_message": "hello, how are you d...",
"unread_count": 0,
"with_user_img": "profile_image_url"
},
...
],
}

Retrieve single inbox message object
Get /messenger/api/v0/inbox/pk
{
"id": 37,
"with_user": "staff",
"last_message": "hello, how are you d...",
"unread_count": 0,
"with_user_img": "profile_image_url"
}
```

Update inbox message object
PATCH /messenger/api/v0/inbox/pk
{
"unread_count": 2,
}
```

Note:
- user can view and update only his inbox resources

"""
authentication_classes = (SessionAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
serializer_class = InboxSerializer
pagination_class = MesssengerResultsSetPagination

def get_queryset(self):
return Inbox.user_inbox.find_all(self.request.user)


class ConversationView(viewsets.ModelViewSet):
"""
Return conversation between two users -> All messages sent or received between request.user and
user with given username.
GET /conversation/?with_user=username2
[
{
"id": 2093,
"sender": "honor",
"receiver": "edx",
"message": "what's the progress on task1 ?",
"created": "08/31/21-01:09 PM",
"sender_img": "sender_profile_image url"
},
...
]
"""
authentication_classes = (SessionAuthentication,)
permission_classes = (permissions.IsAuthenticated, )
serializer_class = MessageSerializer
pagination_class = MesssengerResultsSetPagination

def get_queryset(self):
with_user = self.request.GET.get('with_user', '')
if not with_user:
raise NotFound(_('with_user param is required.'))

try:
with_user = User.objects.get(username=with_user)
return Message.chat.history(self.request.user, with_user)
except User.DoesNotExist:
raise NotFound(_('User with username: {} does not exist.'.format(with_user)))


class MessageCreateView(generics.CreateAPIView):
"""
Create Single Message -
POST /messenger/api/v0/message/
{
"receiver": "receiver_username",
"message": "sample message text"
}
Return newly created message
{
"id": 2092,
"sender": "sender_username",
"receiver": "receiver_username",
"message": "sample message text",
"created": "08/31/21-01:08 PM",
"sender_img": "sender_profile_image_url"
}

"""
authentication_classes = (SessionAuthentication,)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we aren't consistent with this authentication class, this isn't being used in Bulk creation.

permission_classes = (permissions.IsAuthenticated,)
serializer_class = MessageSerializer


class BulkMessageView(viewsets.ViewSet):
"""
Create Bulk messages from request.user

POST /messenger/api/v0/bulk_message/
POST DATA: {
"receivers": [username1, username2]
"message": "message sample text"
}
"""
serializer_class = BulkMessageSerializer
permission_classes = (permissions.IsAuthenticated,)

def bulk_message(self, *ags, **kwargs):
serializer = BulkMessageSerializer(data=self.request.data)
if serializer.is_valid(raise_exception=True):
created_msgs = serializer.bulk_create(request=self.request)
return Response(
data=InboxSerializer(
Inbox.objects.filter(last_message__in=created_msgs),
context={'request': self.request},
many=True
).data,
status=status.HTTP_200_OK
)
Loading