Skip to content

Commit c08b328

Browse files
committed
Merge remote-tracking branch 'origin/master'
# Conflicts: # chats/serializers.py
2 parents e4da2b3 + 7fda482 commit c08b328

24 files changed

Lines changed: 772 additions & 86 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ coverage.xml
5858

5959
# Django stuff:
6060
*.log
61+
*.log.zip
6162
local_settings.py
6263
*.sqlite3
6364
*.sqlite3-journal

Dockerfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ COPY poetry.lock pyproject.toml /procollab/
2222
RUN poetry config virtualenvs.create false \
2323
&& poetry install --no-root
2424

25-
EXPOSE 8000
26-
2725
RUN mkdir /procollab/staticfiles
2826
RUN mkdir /procollab/static
2927

chats/serializers.py

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,37 @@
1212

1313
class DirectChatListSerializer(serializers.ModelSerializer):
1414
last_message = serializers.SerializerMethodField()
15-
users = serializers.SerializerMethodField(read_only=True)
15+
opponent = serializers.SerializerMethodField()
1616

17-
@classmethod
18-
def get_users(cls, chat: ProjectChat):
19-
return UserListSerializer(chat.get_users(), many=True).data
17+
def get_opponent(self, chat: DirectChat):
18+
user = self.context.get("opponent")
19+
return UserDetailSerializer(user).data
2020

2121
@classmethod
2222
def get_last_message(cls, chat: DirectChat):
2323
return DirectChatMessageListSerializer(chat.get_last_message()).data
2424

25-
def get_title(self, chat: DirectChat):
26-
request = self.context.get("request")
27-
user = request.user
28-
return chat.get_other_user(user).get_full_name()
29-
30-
def get_image_address(self, chat: DirectChat):
31-
request = self.context.get("request")
32-
user = request.user
33-
return chat.get_other_user(user).avatar
34-
3525
class Meta:
3626
model = DirectChat
37-
fields = ["id", "users", "last_message", "title", "image_address"]
27+
fields = [
28+
"id",
29+
"opponent",
30+
"last_message",
31+
]
3832

3933

4034
class DirectChatDetailSerializer(serializers.ModelSerializer):
41-
users = serializers.SerializerMethodField(read_only=True)
35+
opponent = serializers.SerializerMethodField()
4236

43-
@classmethod
44-
def get_users(cls, chat: ProjectChat):
45-
return UserListSerializer(chat.get_users(), many=True).data
37+
def get_opponent(self, chat: DirectChat):
38+
user = self.context.get("opponent")
39+
return UserDetailSerializer(user).data
4640

4741
class Meta:
4842
model = DirectChat
4943
fields = [
5044
"id",
51-
"users",
45+
"opponent",
5246
]
5347

5448

@@ -59,17 +53,9 @@ class ProjectChatListSerializer(serializers.ModelSerializer):
5953
def get_last_message(cls, chat: ProjectChat):
6054
return ProjectChatMessageListSerializer(chat.get_last_message()).data
6155

62-
@classmethod
63-
def get_image_address(cls, chat: ProjectChat):
64-
return chat.project.image_address
65-
66-
@classmethod
67-
def get_name(cls, chat: ProjectChat):
68-
return chat.project.name
69-
7056
class Meta:
7157
model = ProjectChat
72-
fields = ["id", "project", "last_message", "name", "image_address"]
58+
fields = ["id", "project", "last_message"]
7359

7460

7561
class ProjectChatDetailSerializer(serializers.ModelSerializer):

chats/views.py

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
DirectChatDetailSerializer,
2323
)
2424
from chats.utils import get_all_files
25+
from files.models import UserFile
2526
from files.serializers import UserFileSerializer
2627

2728
User = get_user_model()
@@ -35,6 +36,31 @@ def get_queryset(self):
3536
user = self.request.user
3637
return user.direct_chats.all()
3738

39+
def get(self, request, *args, **kwargs):
40+
chats = self.get_queryset()
41+
serialized_chats = []
42+
for chat in chats:
43+
# fixme: move to function like get_user() and get_opponent()
44+
chat_id = chat.id
45+
user1_id, user2_id = map(int, chat_id.split("_"))
46+
47+
try:
48+
user1 = User.objects.get(pk=user1_id)
49+
user2 = User.objects.get(pk=user2_id)
50+
except User.DoesNotExist:
51+
# fixme: show deleted profile
52+
continue
53+
54+
if user1 == request.user:
55+
opponent = user2
56+
else: # fixme: if user1 == user2
57+
opponent = user1
58+
59+
context = {"opponent": opponent}
60+
serialized_chat = DirectChatListSerializer(chat, context=context).data
61+
serialized_chats.append(serialized_chat)
62+
return Response(serialized_chats, status=status.HTTP_200_OK)
63+
3864

3965
class ProjectChatList(ListAPIView):
4066
serializer_class = ProjectChatListSerializer
@@ -69,16 +95,17 @@ def get(self, request, *args, **kwargs) -> Response:
6995
user1 = User.objects.get(pk=user1_id)
7096
user2 = User.objects.get(pk=user2_id)
7197

72-
data = DirectChatDetailSerializer(DirectChat.get_chat(user1, user2)).data
73-
7498
if user1 == request.user:
75-
# may be is better to use serializer or return dict -
76-
# {"first_name": user2.first_name, "last_name": user2.last_name}
77-
data["name"] = f"{user2.first_name} {user2.last_name}"
78-
data["image_address"] = user2.avatar
99+
opponent = user2
79100
else:
80-
data["name"] = f"{user1.first_name} {user1.last_name}"
81-
data["image_address"] = user1.avatar
101+
opponent = user1
102+
context = {"opponent": opponent}
103+
data = DirectChatDetailSerializer(
104+
DirectChat.get_chat(user1, user2), context=context
105+
).data
106+
107+
data["name"] = f"{opponent.first_name} {opponent.last_name}"
108+
data["image_address"] = opponent.avatar
82109

83110
return Response(
84111
status=status.HTTP_200_OK,
@@ -124,12 +151,15 @@ class ProjectChatMessageList(ListCreateAPIView):
124151
pagination_class = MessageListPagination
125152

126153
def get_queryset(self):
127-
return (
128-
ProjectChat.objects.get(id=self.kwargs["pk"])
129-
.messages.filter(is_deleted=False)
130-
.order_by("-created_at")
131-
.all()
132-
)
154+
try:
155+
return (
156+
ProjectChat.objects.get(id=self.kwargs["pk"])
157+
.messages.filter(is_deleted=False)
158+
.order_by("-created_at")
159+
.all()
160+
)
161+
except ProjectChat.DoesNotExist:
162+
return ProjectChat.objects.none()
133163

134164
def post(self, request, *args, **kwargs):
135165
# TODO: try to create a message in a chat. If chat doesn't exist, create it and then create a message.
@@ -155,6 +185,8 @@ class ProjectChatFileList(ListCreateAPIView):
155185
permission_classes = [IsProjectChatMember]
156186

157187
def get_queryset(self):
158-
messages = ProjectChat.objects.get(id=self.kwargs["pk"]).messages.all()
159-
160-
return get_all_files(messages)
188+
try:
189+
messages = ProjectChat.objects.get(id=self.kwargs["pk"]).messages.all()
190+
return get_all_files(messages)
191+
except ProjectChat.DoesNotExist:
192+
return UserFile.objects.none()

core/log/middleware.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from loguru import logger
2+
from django.conf import settings
3+
import logging
4+
from core.log.utils import InterceptHandler
5+
6+
7+
class CustomLoguruMiddleware:
8+
def __init__(self, get_response):
9+
self.get_response = get_response
10+
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
11+
12+
if settings.DEBUG:
13+
logger.add(
14+
f"{settings.BASE_DIR}/log/debug.log",
15+
level="DEBUG",
16+
**settings.LOGURU_LOGGING,
17+
)
18+
logger.add(
19+
f"{settings.BASE_DIR}/log/info.log",
20+
level="INFO",
21+
**settings.LOGURU_LOGGING,
22+
)
23+
logger.add(
24+
f"{settings.BASE_DIR}/log/warning.log",
25+
level="WARNING",
26+
**settings.LOGURU_LOGGING,
27+
)
28+
29+
def __call__(self, request):
30+
response = self.get_response(request)
31+
logger.info(f"{request.method} {request.get_full_path()}")
32+
return response
33+
34+
def process_exception(self, request, exception):
35+
logger.warning(f"{exception} http_path={request.get_full_path()}")

core/log/utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import logging
2+
import sys
3+
from loguru import logger
4+
5+
6+
class InterceptHandler(logging.Handler):
7+
def emit(self, record):
8+
try:
9+
level = logger.level(record.levelname).name
10+
except ValueError:
11+
level = record.levelno
12+
13+
frame, depth = sys._getframe(6), 6
14+
while frame and frame.f_code.co_filename == logging.__file__:
15+
frame = frame.f_back
16+
depth += 1
17+
18+
logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())

docker-compose.dev-ci.yml

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,64 @@
1-
version: "3.4"
1+
version: '3.9'
2+
23
services:
3-
server:
4-
ports:
5-
- "8000:8000"
4+
web:
5+
container_name: web
66
build:
77
context: .
8-
dockerfile: Dockerfile
8+
dockerfile: ./Dockerfile
9+
image: ghcr.io/procollab-github/api:latest
10+
restart: always
11+
volumes:
12+
- log:/procollab/log
913
env_file:
1014
- .env
11-
restart: always
12-
networks:
13-
template-network:
14-
15-
networks:
16-
template-network:
17-
15+
environment:
16+
HOST: 0.0.0.0
17+
expose:
18+
- 8000
19+
grafana:
20+
container_name: grafana
21+
image: grafana/grafana:latest
22+
expose:
23+
- 3000
24+
volumes:
25+
- grafana-data:/var/lib/grafana
26+
- grafana-configs:/etc/grafana
27+
environment:
28+
- GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s:%(http_port)s/grafana
29+
- GF_SERVER_SERVE_FROM_SUB_PATH=true
30+
prometheus:
31+
container_name: prometheus
32+
image: prom/prometheus:v2.36.0
33+
expose:
34+
- 9090
35+
volumes:
36+
- prom-data:/prometheus
37+
- ./prometheus:/etc/prometheus
38+
node-exporter:
39+
container_name: node-exporter
40+
image: prom/node-exporter:v1.3.1
41+
expose:
42+
- 9100
43+
volumes:
44+
- /proc:/host/proc:ro
45+
- /sys:/host/sys:ro
46+
- /:/rootfs:ro
47+
command:
48+
- '--path.procfs=/host/proc'
49+
- '--path.sysfs=/host/sys'
50+
- '--collector.filesystem.mount-points-exclude'
51+
- '^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)'
52+
nginx:
53+
container_name: nginx
54+
build: ./nginx
55+
depends_on:
56+
- web
57+
ports:
58+
- 8000:80
1859
volumes:
19-
db-volume:
60+
grafana-data:
61+
grafana-configs:
62+
prom-data:
63+
prom-configs:
64+
log:

0 commit comments

Comments
 (0)