baseapp-backend 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- baseapp-backend-0.0.1/PKG-INFO +12 -0
- baseapp-backend-0.0.1/README.md +1 -0
- baseapp-backend-0.0.1/baseapp/__init__.py +0 -0
- baseapp-backend-0.0.1/baseapp/activity_log/__init__.py +0 -0
- baseapp-backend-0.0.1/baseapp/activity_log/admin.py +35 -0
- baseapp-backend-0.0.1/baseapp/activity_log/apps.py +8 -0
- baseapp-backend-0.0.1/baseapp/activity_log/base.py +218 -0
- baseapp-backend-0.0.1/baseapp/activity_log/graphql/__init__.py +0 -0
- baseapp-backend-0.0.1/baseapp/activity_log/graphql/filters.py +26 -0
- baseapp-backend-0.0.1/baseapp/activity_log/graphql/interfaces.py +46 -0
- baseapp-backend-0.0.1/baseapp/activity_log/graphql/mutations.py +284 -0
- baseapp-backend-0.0.1/baseapp/activity_log/graphql/object_types.py +69 -0
- baseapp-backend-0.0.1/baseapp/activity_log/graphql/queries.py +8 -0
- baseapp-backend-0.0.1/baseapp/activity_log/graphql/subscriptions.py +117 -0
- baseapp-backend-0.0.1/baseapp/activity_log/middleware.py +84 -0
- baseapp-backend-0.0.1/baseapp/activity_log/migrations/0001_initial.py +290 -0
- baseapp-backend-0.0.1/baseapp/activity_log/migrations/0002_message_set_last_message_and_more.py +111 -0
- baseapp-backend-0.0.1/baseapp/activity_log/migrations/__init__.py +0 -0
- baseapp-backend-0.0.1/baseapp/activity_log/models.py +58 -0
- baseapp-backend-0.0.1/baseapp/activity_log/permissions.py +89 -0
- baseapp-backend-0.0.1/baseapp/activity_log/tests/__init__.py +0 -0
- baseapp-backend-0.0.1/baseapp/activity_log/tests/conftest.py +2 -0
- baseapp-backend-0.0.1/baseapp/activity_log/tests/factories.py +32 -0
- baseapp-backend-0.0.1/baseapp/activity_log/tests/test_graphql_mutations.py +428 -0
- baseapp-backend-0.0.1/baseapp/activity_log/tests/test_graphql_queries.py +271 -0
- baseapp-backend-0.0.1/baseapp/activity_log/tests/test_graphql_subscriptions.py +187 -0
- baseapp-backend-0.0.1/baseapp/activity_log/tests/test_utils.py +70 -0
- baseapp-backend-0.0.1/baseapp/activity_log/triggers.py +111 -0
- baseapp-backend-0.0.1/baseapp/activity_log/utils.py +65 -0
- baseapp-backend-0.0.1/baseapp_backend.egg-info/PKG-INFO +12 -0
- baseapp-backend-0.0.1/baseapp_backend.egg-info/SOURCES.txt +41 -0
- baseapp-backend-0.0.1/baseapp_backend.egg-info/dependency_links.txt +1 -0
- baseapp-backend-0.0.1/baseapp_backend.egg-info/top_level.txt +2 -0
- baseapp-backend-0.0.1/pyproject.toml +10 -0
- baseapp-backend-0.0.1/setup.cfg +25 -0
- baseapp-backend-0.0.1/setup.py +11 -0
- baseapp-backend-0.0.1/testproject/__init__.py +0 -0
- baseapp-backend-0.0.1/testproject/graphql.py +25 -0
- baseapp-backend-0.0.1/testproject/settings.py +56 -0
- baseapp-backend-0.0.1/testproject/setup.py +3 -0
- baseapp-backend-0.0.1/testproject/urls.py +10 -0
- baseapp-backend-0.0.1/testproject/wsgi.py +16 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: baseapp-backend
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: BaseApp
|
|
5
|
+
Home-page: https://github.com/silverlogic/baseapp-backend
|
|
6
|
+
Author: The SilverLogic
|
|
7
|
+
Author-email: dev@tsl.io
|
|
8
|
+
License: BSD-3-Clause # Example license
|
|
9
|
+
Description: # BaseApp
|
|
10
|
+
Platform: UNKNOWN
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# BaseApp
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import swapper
|
|
2
|
+
from django.contrib import admin
|
|
3
|
+
|
|
4
|
+
ChatRoom = swapper.load_model("baseapp_chats", "ChatRoom")
|
|
5
|
+
ChatRoomParticipant = swapper.load_model("baseapp_chats", "ChatRoomParticipant")
|
|
6
|
+
Message = swapper.load_model("baseapp_chats", "Message")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ChatRoomParticipantInline(admin.TabularInline):
|
|
10
|
+
model = ChatRoomParticipant
|
|
11
|
+
extra = 0
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@admin.register(ChatRoomParticipant)
|
|
15
|
+
class ChatRoomParticipantAdmin(admin.ModelAdmin):
|
|
16
|
+
list_display = ("profile", "room", "role")
|
|
17
|
+
list_filter = ["created", "role"]
|
|
18
|
+
search_fields = ["profile", "room"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@admin.register(Message)
|
|
22
|
+
class MessageAdmin(admin.ModelAdmin):
|
|
23
|
+
list_display = ("profile", "verb", "action_object", "room", "timesince")
|
|
24
|
+
list_filter = ["verb"]
|
|
25
|
+
search_fields = ["content", "profile", "action_object", "room"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@admin.register(ChatRoom)
|
|
29
|
+
class ChatRoomAdmin(admin.ModelAdmin):
|
|
30
|
+
list_display = ("room",)
|
|
31
|
+
search_fields = ["room"]
|
|
32
|
+
inlines = [ChatRoomParticipantInline]
|
|
33
|
+
|
|
34
|
+
def room(self, obj):
|
|
35
|
+
return obj.__str__()
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import swapper
|
|
2
|
+
from baseapp_core.graphql.models import RelayModel
|
|
3
|
+
from baseapp_core.models import random_name_in
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
6
|
+
from django.contrib.contenttypes.models import ContentType
|
|
7
|
+
from django.db import models
|
|
8
|
+
from django.utils import timezone
|
|
9
|
+
from django.utils.timesince import timesince as djtimesince
|
|
10
|
+
from django.utils.translation import gettext_lazy as _
|
|
11
|
+
from model_utils.models import TimeStampedModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AbstractBaseChatRoom(TimeStampedModel, RelayModel):
|
|
15
|
+
title = models.CharField(max_length=255, blank=True, null=True)
|
|
16
|
+
image = models.ImageField(
|
|
17
|
+
_("image"), upload_to=random_name_in("chat_room_images"), blank=True, null=True
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
created_by = models.ForeignKey(
|
|
21
|
+
settings.AUTH_USER_MODEL, related_name="created_rooms", on_delete=models.CASCADE
|
|
22
|
+
)
|
|
23
|
+
last_message = models.ForeignKey(
|
|
24
|
+
swapper.get_model_name("baseapp_chats", "Message"),
|
|
25
|
+
null=True,
|
|
26
|
+
blank=True,
|
|
27
|
+
on_delete=models.SET_NULL,
|
|
28
|
+
)
|
|
29
|
+
last_message_time = models.DateTimeField(null=True, blank=True, default=timezone.now)
|
|
30
|
+
participants_count = models.IntegerField(default=0)
|
|
31
|
+
messages_count = models.IntegerField(default=0)
|
|
32
|
+
|
|
33
|
+
class Meta:
|
|
34
|
+
abstract = True
|
|
35
|
+
ordering = ["-last_message_time", "-created"]
|
|
36
|
+
|
|
37
|
+
def __str__(self):
|
|
38
|
+
return str(self.pk)
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def get_graphql_object_type(cls):
|
|
42
|
+
from .graphql.object_types import ChatRoomObjectType
|
|
43
|
+
|
|
44
|
+
return ChatRoomObjectType
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AbstractBaseMessage(TimeStampedModel, RelayModel):
|
|
48
|
+
class Verbs(models.IntegerChoices):
|
|
49
|
+
SENT_MESSAGE = 100, _("sent a message")
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def description(self):
|
|
53
|
+
return self.label
|
|
54
|
+
|
|
55
|
+
content = models.TextField(null=True, blank=True)
|
|
56
|
+
|
|
57
|
+
user = models.ForeignKey(
|
|
58
|
+
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True
|
|
59
|
+
)
|
|
60
|
+
profile = models.ForeignKey(
|
|
61
|
+
swapper.get_model_name("baseapp_profiles", "Profile"),
|
|
62
|
+
on_delete=models.CASCADE,
|
|
63
|
+
null=True,
|
|
64
|
+
blank=True,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
verb = models.IntegerField(choices=Verbs.choices, default=Verbs.SENT_MESSAGE, db_index=True)
|
|
68
|
+
|
|
69
|
+
room = models.ForeignKey(
|
|
70
|
+
swapper.get_model_name("baseapp_chats", "ChatRoom"),
|
|
71
|
+
blank=True,
|
|
72
|
+
null=True,
|
|
73
|
+
related_name="messages",
|
|
74
|
+
on_delete=models.CASCADE,
|
|
75
|
+
db_index=True,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
in_reply_to = models.ForeignKey(
|
|
79
|
+
swapper.get_model_name("baseapp_chats", "Message"),
|
|
80
|
+
on_delete=models.SET_NULL,
|
|
81
|
+
null=True,
|
|
82
|
+
blank=True,
|
|
83
|
+
related_name="replies",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
action_object_content_type = models.ForeignKey(
|
|
87
|
+
ContentType,
|
|
88
|
+
blank=True,
|
|
89
|
+
null=True,
|
|
90
|
+
related_name="action_object",
|
|
91
|
+
on_delete=models.CASCADE,
|
|
92
|
+
db_index=True,
|
|
93
|
+
)
|
|
94
|
+
action_object_object_id = models.IntegerField(blank=True, null=True, db_index=True)
|
|
95
|
+
action_object = GenericForeignKey("action_object_content_type", "action_object_object_id")
|
|
96
|
+
|
|
97
|
+
extra_data = models.JSONField(blank=True, null=True)
|
|
98
|
+
|
|
99
|
+
class Meta:
|
|
100
|
+
abstract = True
|
|
101
|
+
ordering = ["-created"]
|
|
102
|
+
|
|
103
|
+
def __str__(self):
|
|
104
|
+
ctx = {
|
|
105
|
+
"actor": self.profile,
|
|
106
|
+
"verb": self.__class__.Verbs(self.verb).label,
|
|
107
|
+
"action_object": self.action_object,
|
|
108
|
+
"target": self.room,
|
|
109
|
+
"timesince": self.timesince(),
|
|
110
|
+
}
|
|
111
|
+
if self.room:
|
|
112
|
+
if self.action_object:
|
|
113
|
+
return (
|
|
114
|
+
_("%(actor)s %(verb)s %(action_object)s on %(target)s %(timesince)s ago") % ctx
|
|
115
|
+
)
|
|
116
|
+
return _("%(actor)s %(verb)s on %(target)s %(timesince)s ago") % ctx
|
|
117
|
+
if self.action_object:
|
|
118
|
+
return _("%(actor)s %(verb)s %(action_object)s %(timesince)s ago") % ctx
|
|
119
|
+
return _("%(actor)s %(verb)s %(timesince)s ago") % ctx
|
|
120
|
+
|
|
121
|
+
def timesince(self, now=None):
|
|
122
|
+
"""
|
|
123
|
+
Shortcut for the ``django.utils.timesince.timesince`` function of the
|
|
124
|
+
current timestamp.
|
|
125
|
+
"""
|
|
126
|
+
return (
|
|
127
|
+
djtimesince(self.created, now).encode("utf8").replace(b"\xc2\xa0", b" ").decode("utf8")
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def get_graphql_object_type(cls):
|
|
132
|
+
from .graphql.object_types import MessageObjectType
|
|
133
|
+
|
|
134
|
+
return MessageObjectType
|
|
135
|
+
|
|
136
|
+
def save(self, *args, **kwargs):
|
|
137
|
+
created = self._state.adding
|
|
138
|
+
super().save(*args, **kwargs)
|
|
139
|
+
|
|
140
|
+
if created:
|
|
141
|
+
from baseapp_chats.graphql.subscriptions import (
|
|
142
|
+
ChatRoomOnMessagesCountUpdate,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
for participant in self.room.participants.all():
|
|
146
|
+
if participant.profile_id != self.profile_id:
|
|
147
|
+
ChatRoomOnMessagesCountUpdate.send_updated_chat_count(
|
|
148
|
+
profile=participant.profile, profile_id=participant.profile.relay_id
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class AbstractChatRoomParticipant(TimeStampedModel, RelayModel):
|
|
153
|
+
class ChatRoomParticipantRoles(models.IntegerChoices):
|
|
154
|
+
MEMBER = 1, _("member")
|
|
155
|
+
ADMIM = 2, _("admin")
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def description(self):
|
|
159
|
+
return self.label
|
|
160
|
+
|
|
161
|
+
profile = models.ForeignKey(
|
|
162
|
+
swapper.get_model_name("baseapp_profiles", "Profile"),
|
|
163
|
+
on_delete=models.CASCADE,
|
|
164
|
+
null=True,
|
|
165
|
+
blank=True,
|
|
166
|
+
)
|
|
167
|
+
room = models.ForeignKey(
|
|
168
|
+
swapper.get_model_name("baseapp_chats", "ChatRoom"),
|
|
169
|
+
related_name="participants",
|
|
170
|
+
on_delete=models.CASCADE,
|
|
171
|
+
)
|
|
172
|
+
role = models.IntegerField(
|
|
173
|
+
choices=ChatRoomParticipantRoles.choices, default=ChatRoomParticipantRoles.MEMBER
|
|
174
|
+
)
|
|
175
|
+
accepted_at = models.DateTimeField(null=True, blank=True)
|
|
176
|
+
|
|
177
|
+
class Meta:
|
|
178
|
+
abstract = True
|
|
179
|
+
ordering = ["created"]
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def get_graphql_object_type(cls):
|
|
183
|
+
from .graphql.object_types import ChatRoomParticipantObjectType
|
|
184
|
+
|
|
185
|
+
return ChatRoomParticipantObjectType
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class AbstractUnreadMessageCount(RelayModel):
|
|
189
|
+
room = models.ForeignKey(
|
|
190
|
+
swapper.get_model_name("baseapp_chats", "ChatRoom"),
|
|
191
|
+
on_delete=models.CASCADE,
|
|
192
|
+
)
|
|
193
|
+
profile = models.ForeignKey(
|
|
194
|
+
swapper.get_model_name("baseapp_profiles", "Profile"), on_delete=models.CASCADE
|
|
195
|
+
)
|
|
196
|
+
count = models.IntegerField(default=0)
|
|
197
|
+
|
|
198
|
+
class Meta:
|
|
199
|
+
abstract = True
|
|
200
|
+
ordering = ["id"]
|
|
201
|
+
unique_together = ["room_id", "profile_id"]
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class AbstractMessageStatus(RelayModel):
|
|
205
|
+
message = models.ForeignKey(
|
|
206
|
+
swapper.get_model_name("baseapp_chats", "Message"),
|
|
207
|
+
on_delete=models.CASCADE,
|
|
208
|
+
related_name="statuses",
|
|
209
|
+
)
|
|
210
|
+
profile = models.ForeignKey(
|
|
211
|
+
swapper.get_model_name("baseapp_profiles", "Profile"), on_delete=models.CASCADE
|
|
212
|
+
)
|
|
213
|
+
is_read = models.BooleanField(default=False)
|
|
214
|
+
read_at = models.DateTimeField(null=True, blank=True)
|
|
215
|
+
|
|
216
|
+
class Meta:
|
|
217
|
+
abstract = True
|
|
218
|
+
unique_together = ["message_id", "profile_id"]
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import django_filters
|
|
2
|
+
import swapper
|
|
3
|
+
from baseapp_core.graphql import get_pk_from_relay_id
|
|
4
|
+
from django.db.models import Q
|
|
5
|
+
|
|
6
|
+
ChatRoom = swapper.load_model("baseapp_chats", "ChatRoom")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ChatRoomFilter(django_filters.FilterSet):
|
|
10
|
+
q = django_filters.CharFilter(method="filter_q")
|
|
11
|
+
profile_id = django_filters.CharFilter(method="filter_profile_id")
|
|
12
|
+
|
|
13
|
+
order_by = django_filters.OrderingFilter(fields=(("created", "created"),))
|
|
14
|
+
|
|
15
|
+
class Meta:
|
|
16
|
+
model = ChatRoom
|
|
17
|
+
fields = ["q", "order_by"]
|
|
18
|
+
|
|
19
|
+
def filter_q(self, queryset, name, value):
|
|
20
|
+
return queryset.filter(
|
|
21
|
+
Q(title__icontains=value) | Q(participants__profile__name__icontains=value)
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def filter_profile_id(self, queryset, name, value):
|
|
25
|
+
pk = get_pk_from_relay_id(value)
|
|
26
|
+
return queryset.filter(participants__profile_id=pk)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import graphene
|
|
2
|
+
import swapper
|
|
3
|
+
from baseapp_core.graphql import get_object_type_for_model
|
|
4
|
+
from django.db.models import Sum
|
|
5
|
+
from graphene import relay
|
|
6
|
+
from graphene_django.filter import DjangoFilterConnectionField
|
|
7
|
+
|
|
8
|
+
ChatRoom = swapper.load_model("baseapp_chats", "ChatRoom")
|
|
9
|
+
UnreadMessageCount = swapper.load_model("baseapp_chats", "UnreadMessageCount")
|
|
10
|
+
Block = swapper.load_model("baseapp_blocks", "Block")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ActivityLogInterface(relay.Node):
|
|
14
|
+
activity_logs = DjangoFilterConnectionField(ActivityEventObjectType)
|
|
15
|
+
|
|
16
|
+
def resolve_chat_rooms(self, info, **kwargs):
|
|
17
|
+
if not info.context.user.has_perm("baseapp_chats.list_chatrooms", self):
|
|
18
|
+
return ChatRoom.objects.none()
|
|
19
|
+
|
|
20
|
+
qs = ChatRoom.objects.filter(
|
|
21
|
+
participants__profile_id=self.pk,
|
|
22
|
+
).order_by("-last_message_time", "-created")
|
|
23
|
+
|
|
24
|
+
# Exclude rooms with any blocked participant
|
|
25
|
+
blocking_profile_ids = Block.objects.filter(actor_id=self.pk).values_list(
|
|
26
|
+
"target_id", flat=True
|
|
27
|
+
)
|
|
28
|
+
qs = qs.exclude(participants__profile_id__in=blocking_profile_ids)
|
|
29
|
+
|
|
30
|
+
# Exclude rooms with any participant that blocks the profile (self)
|
|
31
|
+
blocker_profile_ids = Block.objects.filter(target_id=self.pk).values_list(
|
|
32
|
+
"actor_id", flat=True
|
|
33
|
+
)
|
|
34
|
+
qs = qs.exclude(participants__profile_id__in=blocker_profile_ids)
|
|
35
|
+
|
|
36
|
+
return qs
|
|
37
|
+
|
|
38
|
+
def resolve_unread_messages_count(self, info, **kwargs):
|
|
39
|
+
if not info.context.user.has_perm("baseapp_chats.list_chatrooms", self):
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
aggregate_result = UnreadMessageCount.objects.filter(
|
|
43
|
+
profile_id=self.pk,
|
|
44
|
+
).aggregate(total_count=Sum("count"))
|
|
45
|
+
|
|
46
|
+
return aggregate_result["total_count"] or 0
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import graphene
|
|
2
|
+
import swapper
|
|
3
|
+
from baseapp_core.graphql import (
|
|
4
|
+
RelayMutation,
|
|
5
|
+
get_obj_from_relay_id,
|
|
6
|
+
get_pk_from_relay_id,
|
|
7
|
+
login_required,
|
|
8
|
+
)
|
|
9
|
+
from django.contrib.auth import get_user_model
|
|
10
|
+
from django.db.models import Count, Q
|
|
11
|
+
from django.utils import timezone
|
|
12
|
+
from django.utils.translation import gettext_lazy as _
|
|
13
|
+
from graphene_django.types import ErrorType
|
|
14
|
+
|
|
15
|
+
from baseapp_chats.graphql.subscriptions import ChatRoomOnMessagesCountUpdate
|
|
16
|
+
from baseapp_chats.utils import send_message, send_new_chat_message_notification
|
|
17
|
+
|
|
18
|
+
ChatRoom = swapper.load_model("baseapp_chats", "ChatRoom")
|
|
19
|
+
ChatRoomParticipant = swapper.load_model("baseapp_chats", "ChatRoomParticipant")
|
|
20
|
+
Message = swapper.load_model("baseapp_chats", "Message")
|
|
21
|
+
MessageStatus = swapper.load_model("baseapp_chats", "MessageStatus")
|
|
22
|
+
Block = swapper.load_model("baseapp_blocks", "Block")
|
|
23
|
+
User = get_user_model()
|
|
24
|
+
Profile = swapper.load_model("baseapp_profiles", "Profile")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
ChatRoomObjectType = ChatRoom.get_graphql_object_type()
|
|
28
|
+
ProfileObjectType = Profile.get_graphql_object_type()
|
|
29
|
+
MessageObjectType = Message.get_graphql_object_type()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ChatRoomCreate(RelayMutation):
|
|
33
|
+
room = graphene.Field(ChatRoomObjectType._meta.connection.Edge)
|
|
34
|
+
profile = graphene.Field(ProfileObjectType)
|
|
35
|
+
|
|
36
|
+
class Input:
|
|
37
|
+
profile_id = graphene.ID(required=True)
|
|
38
|
+
participants = graphene.List(graphene.ID, required=True)
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
@login_required
|
|
42
|
+
def mutate_and_get_payload(cls, root, info, profile_id, participants, **input):
|
|
43
|
+
profile = get_obj_from_relay_id(info, profile_id)
|
|
44
|
+
|
|
45
|
+
if not info.context.user.has_perm("baseapp_profiles.use_profile", profile):
|
|
46
|
+
return ChatRoomCreate(
|
|
47
|
+
errors=[
|
|
48
|
+
ErrorType(
|
|
49
|
+
field="profile_id",
|
|
50
|
+
messages=[_("You don't have permission to send a message as this profile")],
|
|
51
|
+
)
|
|
52
|
+
]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
participants = [get_obj_from_relay_id(info, participant) for participant in participants]
|
|
56
|
+
participants = [participant for participant in participants if participant is not None]
|
|
57
|
+
participants_ids = [participant.pk for participant in participants]
|
|
58
|
+
|
|
59
|
+
# Check if participants are blocked
|
|
60
|
+
if Block.objects.filter(
|
|
61
|
+
Q(actor_id=profile.id, target_id__in=participants_ids)
|
|
62
|
+
| Q(actor_id__in=participants_ids, target_id=profile.id)
|
|
63
|
+
).exists():
|
|
64
|
+
return ChatRoomCreate(
|
|
65
|
+
errors=[
|
|
66
|
+
ErrorType(
|
|
67
|
+
field="participants",
|
|
68
|
+
messages=[_("You can't create a chatroom with those participants")],
|
|
69
|
+
)
|
|
70
|
+
]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
participants.append(profile)
|
|
74
|
+
|
|
75
|
+
if len(participants) < 2:
|
|
76
|
+
return ChatRoomCreate(
|
|
77
|
+
errors=[
|
|
78
|
+
ErrorType(
|
|
79
|
+
field="participants",
|
|
80
|
+
messages=[_("You need add at least one participant")],
|
|
81
|
+
)
|
|
82
|
+
]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if not info.context.user.has_perm(
|
|
86
|
+
"baseapp_chats.add_chatroom", {"profile": profile, "participants": participants}
|
|
87
|
+
):
|
|
88
|
+
return ChatRoomCreate(
|
|
89
|
+
errors=[
|
|
90
|
+
ErrorType(
|
|
91
|
+
field="participants",
|
|
92
|
+
messages=[_("You don't have permission to create a room")],
|
|
93
|
+
)
|
|
94
|
+
]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
query_set = ChatRoom.objects.annotate(count=Count("participants")).filter(
|
|
98
|
+
count=len(participants)
|
|
99
|
+
)
|
|
100
|
+
for participant in participants:
|
|
101
|
+
query_set = query_set.filter(
|
|
102
|
+
participants__profile_id=participant.id,
|
|
103
|
+
)
|
|
104
|
+
existent_room = query_set.first()
|
|
105
|
+
|
|
106
|
+
if existent_room:
|
|
107
|
+
return ChatRoomCreate(
|
|
108
|
+
profile=profile,
|
|
109
|
+
room=ChatRoomObjectType._meta.connection.Edge(
|
|
110
|
+
node=existent_room,
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
room = ChatRoom.objects.create(
|
|
115
|
+
created_by=info.context.user, last_message_time=timezone.now()
|
|
116
|
+
)
|
|
117
|
+
for participant in participants:
|
|
118
|
+
ChatRoomParticipant.objects.create(profile=participant, room=room)
|
|
119
|
+
|
|
120
|
+
return ChatRoomCreate(
|
|
121
|
+
profile=profile,
|
|
122
|
+
room=ChatRoomObjectType._meta.connection.Edge(
|
|
123
|
+
node=room,
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class ChatRoomSendMessage(RelayMutation):
|
|
129
|
+
message = graphene.Field(MessageObjectType._meta.connection.Edge)
|
|
130
|
+
|
|
131
|
+
class Input:
|
|
132
|
+
room_id = graphene.ID(required=True)
|
|
133
|
+
profile_id = graphene.ID(required=True)
|
|
134
|
+
content = graphene.String(required=True)
|
|
135
|
+
in_reply_to_id = graphene.ID(required=False)
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
@login_required
|
|
139
|
+
def mutate_and_get_payload(
|
|
140
|
+
cls, root, info, room_id, content, profile_id, in_reply_to_id=None, **input
|
|
141
|
+
):
|
|
142
|
+
room = get_obj_from_relay_id(info, room_id)
|
|
143
|
+
profile = get_obj_from_relay_id(info, profile_id)
|
|
144
|
+
|
|
145
|
+
in_reply_to = None
|
|
146
|
+
if in_reply_to_id:
|
|
147
|
+
in_reply_to_pk = get_pk_from_relay_id(in_reply_to_id)
|
|
148
|
+
in_reply_to = Message.objects.filter(
|
|
149
|
+
pk=in_reply_to_pk,
|
|
150
|
+
room=room,
|
|
151
|
+
).first()
|
|
152
|
+
|
|
153
|
+
if not info.context.user.has_perm("baseapp_profiles.use_profile", profile):
|
|
154
|
+
return ChatRoomSendMessage(
|
|
155
|
+
errors=[
|
|
156
|
+
ErrorType(
|
|
157
|
+
field="profile_id",
|
|
158
|
+
messages=[_("You don't have permission to send a message as this profile")],
|
|
159
|
+
)
|
|
160
|
+
]
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if not room or not info.context.user.has_perm(
|
|
164
|
+
"baseapp_chats.add_message", {"profile": profile, "room": room}
|
|
165
|
+
):
|
|
166
|
+
return ChatRoomSendMessage(
|
|
167
|
+
errors=[
|
|
168
|
+
ErrorType(
|
|
169
|
+
field="room_id",
|
|
170
|
+
messages=[_("You don't have permission to send a message in this room")],
|
|
171
|
+
)
|
|
172
|
+
]
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if len(content) < 1:
|
|
176
|
+
return ChatRoomSendMessage(
|
|
177
|
+
errors=[
|
|
178
|
+
ErrorType(
|
|
179
|
+
field="content",
|
|
180
|
+
messages=[_("You need to write something")],
|
|
181
|
+
)
|
|
182
|
+
]
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if len(content) > 1000:
|
|
186
|
+
return ChatRoomSendMessage(
|
|
187
|
+
errors=[
|
|
188
|
+
ErrorType(
|
|
189
|
+
field="content",
|
|
190
|
+
messages=[_("You can't write more than 1000 characters")],
|
|
191
|
+
)
|
|
192
|
+
]
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
message = send_message(
|
|
196
|
+
profile=profile,
|
|
197
|
+
user=info.context.user,
|
|
198
|
+
content=content,
|
|
199
|
+
room=room,
|
|
200
|
+
verb=Message.Verbs.SENT_MESSAGE,
|
|
201
|
+
room_id=room_id,
|
|
202
|
+
in_reply_to=in_reply_to,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
send_new_chat_message_notification(room, message, info)
|
|
206
|
+
|
|
207
|
+
return ChatRoomSendMessage(
|
|
208
|
+
message=MessageObjectType._meta.connection.Edge(
|
|
209
|
+
node=message,
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class ChatRoomReadMessages(RelayMutation):
|
|
215
|
+
room = graphene.Field(ChatRoomObjectType)
|
|
216
|
+
profile = graphene.Field(ProfileObjectType)
|
|
217
|
+
messages = graphene.List(MessageObjectType)
|
|
218
|
+
|
|
219
|
+
class Input:
|
|
220
|
+
room_id = graphene.ID(required=True)
|
|
221
|
+
profile_id = graphene.ID(required=True)
|
|
222
|
+
message_ids = graphene.List(graphene.ID, required=False)
|
|
223
|
+
|
|
224
|
+
@classmethod
|
|
225
|
+
@login_required
|
|
226
|
+
def mutate_and_get_payload(cls, root, info, room_id, profile_id, message_ids=None, **input):
|
|
227
|
+
room = get_obj_from_relay_id(info, room_id)
|
|
228
|
+
profile = get_obj_from_relay_id(info, profile_id)
|
|
229
|
+
|
|
230
|
+
if not info.context.user.has_perm("baseapp_profiles.use_profile", profile):
|
|
231
|
+
return ChatRoomReadMessages(
|
|
232
|
+
errors=[
|
|
233
|
+
ErrorType(
|
|
234
|
+
field="profile_id",
|
|
235
|
+
messages=[_("You don't have permission to act as this profile")],
|
|
236
|
+
)
|
|
237
|
+
]
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if not ChatRoomParticipant.objects.filter(
|
|
241
|
+
profile_id=profile.pk,
|
|
242
|
+
room=room,
|
|
243
|
+
).exists():
|
|
244
|
+
return ChatRoomReadMessages(
|
|
245
|
+
errors=[
|
|
246
|
+
ErrorType(
|
|
247
|
+
field="participant",
|
|
248
|
+
messages=[_("Participant is not part of the room.")],
|
|
249
|
+
)
|
|
250
|
+
]
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
messages_status_qs = MessageStatus.objects.filter(
|
|
254
|
+
profile_id=profile.pk,
|
|
255
|
+
is_read=False,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
if message_ids:
|
|
259
|
+
message_ids = [get_pk_from_relay_id(message_id) for message_id in message_ids]
|
|
260
|
+
messages_status_qs = messages_status_qs.filter(
|
|
261
|
+
message_id__in=message_ids,
|
|
262
|
+
)
|
|
263
|
+
else:
|
|
264
|
+
messages_status_qs = messages_status_qs.filter(
|
|
265
|
+
message__room_id=room.pk,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
messages = Message.objects.filter(
|
|
269
|
+
pk__in=messages_status_qs.values_list("message_id", flat=True)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
messages_status_qs.update(is_read=True, read_at=timezone.now())
|
|
273
|
+
|
|
274
|
+
ChatRoomOnMessagesCountUpdate.send_updated_chat_count(
|
|
275
|
+
profile=profile, profile_id=profile.relay_id
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return ChatRoomReadMessages(room=room, profile=profile, messages=messages)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class ChatsMutations(object):
|
|
282
|
+
chat_room_create = ChatRoomCreate.Field()
|
|
283
|
+
chat_room_send_message = ChatRoomSendMessage.Field()
|
|
284
|
+
chat_room_read_messages = ChatRoomReadMessages.Field()
|