Skip to content

Commit 429e657

Browse files
authored
Merge pull request #451 from PROCOLLAB-github/flexivanov237-pro-434
Flexivanov237 pro 434
2 parents 5010755 + 7bed9c3 commit 429e657

10 files changed

Lines changed: 90 additions & 37 deletions

File tree

chats/serializers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class DirectChatDetailSerializer(serializers.ModelSerializer):
4444

4545
def get_opponent(self, chat: DirectChat):
4646
user = self.context.get("opponent")
47-
return UserDetailSerializer(
47+
return UserChatSerializer(
4848
user, context={"request": self.context.get("request")}
4949
).data
5050

chats/views.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,19 @@ def get(self, request, *args, **kwargs):
4343
chats = self.get_queryset()
4444
serialized_chats = []
4545
for chat in chats:
46-
user1_id, _ = map(int, chat.id.split("_"))
47-
# TODO сделать проверку на удаление профиля
48-
if user1_id == request.user.id:
49-
opponent = chat.users.all()[1]
50-
else:
51-
opponent = chat.users.first()
52-
53-
context = {"opponent": opponent}
54-
serialized_chat = DirectChatListSerializer(chat, context=context).data
55-
serialized_chats.append(serialized_chat)
46+
try:
47+
user1_id, _ = map(int, chat.id.split("_"))
48+
# TODO сделать проверку на удаление профиля
49+
if user1_id == request.user.id:
50+
opponent = chat.users.all()[1]
51+
else:
52+
opponent = chat.users.first()
53+
54+
context = {"opponent": opponent}
55+
serialized_chat = DirectChatListSerializer(chat, context=context).data
56+
serialized_chats.append(serialized_chat)
57+
except IndexError:
58+
pass
5659
return Response(serialized_chats, status=status.HTTP_200_OK)
5760

5861

@@ -86,9 +89,8 @@ def get(self, request, *args, **kwargs) -> Response:
8689
request.user.id == user1_id or request.user.id == user2_id
8790
), "current user id is not present in raw id"
8891

89-
users = User.objects.filter(pk__in=[user1_id, user2_id])
90-
user1 = users.get(pk=user1_id)
91-
user2 = users.get(pk=user2_id)
92+
user1 = User.objects.get(pk=user1_id)
93+
user2 = User.objects.get(pk=user2_id)
9294

9395
if user1 == request.user:
9496
opponent = user2

mailing/utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from functools import singledispatch
22
from typing import Dict, List, Union, Annotated
3+
4+
from procollab import settings
35
from .constants import MAILING_USERS_BATCH_SIZE
46
from .models import MailingSchema
57
from users.models import CustomUser
@@ -111,7 +113,9 @@ def send_mass_mail(
111113
template_context["user"] = user
112114
html_msg = template.render(Context(template_context))
113115
plain_msg = template.render(Context(template_context))
114-
msg = EmailMultiAlternatives(subject, plain_msg, None, [user.email])
116+
msg = EmailMultiAlternatives(
117+
subject, plain_msg, settings.EMAIL_USER, [user.email]
118+
)
115119
msg.attach_alternative(html_msg, "text/html")
116120
messages.append(msg)
117121

poetry.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

procollab/settings.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
"django_cleanup.apps.CleanupConfig",
105105
"django_rest_passwordreset",
106106
# Plugins
107+
"anymail",
107108
"celery",
108109
"django_celery_beat",
109110
"corsheaders",
@@ -320,12 +321,22 @@
320321

321322
SESSION_COOKIE_SECURE = False
322323

323-
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
324+
EMAIL_BACKEND = "anymail.backends.unisender_go.EmailBackend"
325+
326+
ANYMAIL = {
327+
"UNISENDER_GO_API_KEY": config("UNISENDER_GO_API_KEY", cast=str),
328+
"UNISENDER_GO_API_URL": "https://go1.unisender.ru/ru/transactional/api/v1/",
329+
"UNISENDER_GO_SEND_DEFAULTS": {
330+
"esp_extra": {
331+
"global_language": "ru",
332+
}
333+
},
334+
}
335+
324336
EMAIL_USE_TLS = True
325-
EMAIL_HOST = config("EMAIL_HOST", default="smtp.gmail.com", cast=str)
337+
326338
EMAIL_PORT = config("EMAIL_PORT", default=587, cast=int)
327-
EMAIL_HOST_USER = config("EMAIL_USER", cast=str, default="example@mail.ru")
328-
EMAIL_HOST_PASSWORD = config("EMAIL_PASSWORD", cast=str, default="password")
339+
EMAIL_USER = config("EMAIL_USER", cast=str, default="example@mail.ru")
329340

330341
SELECTEL_ACCOUNT_ID = config("SELECTEL_ACCOUNT_ID", cast=str, default="123456")
331342
SELECTEL_CONTAINER_NAME = config(

procollab/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
path("api/token/verify/", TokenVerifyView.as_view(), name="token_verify"),
5656
path("", include("metrics.urls", namespace="metrics")),
5757
path("django_prometheus/", include("django_prometheus.urls")),
58+
path("anymail/", include("anymail.urls")),
5859
]
5960

6061
if settings.DEBUG:

projects/filters.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,28 +57,36 @@ def vacancy_filter(cls, queryset, name, value):
5757

5858
def filter_by_partner_program(self, queryset, name, value):
5959
program_id = value
60+
6061
user = self.request.user
6162
try:
6263
program = PartnerProgram.objects.get(pk=program_id)
6364
program_status = program.projects_availability
6465
# If available to all users or request.user is an expert of this program.
65-
if program_status == "all_users" or Expert.objects.filter(user=user, programs=program).exists():
66+
if (
67+
program_status == "all_users"
68+
or Expert.objects.filter(user=user, programs=program).exists()
69+
):
6670
profiles_qs = (
6771
PartnerProgramUserProfile.objects.filter(
6872
partner_program=program, project__isnull=False
6973
)
7074
.select_related("project")
7175
.only("project")
7276
)
73-
return queryset.filter(pk__in=[profile.project.pk for profile in profiles_qs])
77+
return queryset.filter(
78+
pk__in=[profile.project.pk for profile in profiles_qs]
79+
)
7480
else:
7581
return Project.objects.none()
7682

7783
except PartnerProgram.DoesNotExist:
7884
return Project.objects.none()
7985

8086
def filter_by_have_expert_rates(self, queryset, name, value):
81-
rated_projects_ids = ProjectScore.objects.values_list("project_id", flat=True).distinct()
87+
rated_projects_ids = ProjectScore.objects.values_list(
88+
"project_id", flat=True
89+
).distinct()
8290
if value:
8391
return queryset.filter(id__in=rated_projects_ids)
8492
return queryset.exclude(id__in=rated_projects_ids)
@@ -95,7 +103,7 @@ def filter_by_have_expert_rates(self, queryset, name, value):
95103
)
96104
is_company = filters.BooleanFilter(
97105
field_name="is_company",
98-
label="is_company\n`1`/`true` is company\n`0`/`false` is not company"
106+
label="is_company\n`1`/`true` is company\n`0`/`false` is not company",
99107
)
100108

101109
# filters by whether there are any vacancies in the project
@@ -112,7 +120,7 @@ def filter_by_have_expert_rates(self, queryset, name, value):
112120
)
113121
is_rated_by_expert = filters.BooleanFilter(
114122
method="filter_by_have_expert_rates",
115-
label=("is_rated_by_expert\n`1`/`true` rated projects\n`0`/`false` dosn't rated")
123+
label=("is_rated_by_expert\n`1`/`true` rated projects\n`0`/`false` dosn't rated"),
116124
)
117125

118126
class Meta:

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pandas = "^2.2.1"
6969
celery = "^5.4.0"
7070
django-celery-beat = "^2.6.0"
7171
weasyprint = "^62.3"
72+
django-anymail = "^12.0"
7273

7374

7475
[build-system]

scripts/celery.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#!/bin/bash
22
cd apps
3-
celery -A procollab worker --beat --loglevel=debug
3+
celery -A procollab worker --beat --loglevel=debug

users/views.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,14 @@ class UserSkillsApproveDeclineView(APIView):
196196
"and `skill_pk` == `skill.id` in the query string."
197197
),
198198
manual_parameters=[USER_PK_PARAM, SKILL_PK_PARAM],
199-
responses={201: UserApproveSkillResponse}
199+
responses={201: UserApproveSkillResponse},
200200
)
201201
def post(self, request, *args, **kwargs) -> Response:
202202
"""Create confirmation of user skill by current user."""
203203
skill_to_object: SkillToObject = self._get_skill_to_object()
204204
data: dict[str, int] = {
205205
"skill_to_object": skill_to_object.id,
206-
"confirmed_by": request.user.id
206+
"confirmed_by": request.user.id,
207207
}
208208
serializer = self.serializer_class(data=data, context={"request": request})
209209
serializer.is_valid(raise_exception=True)
@@ -529,21 +529,21 @@ class RemoteViewSubscriptions(APIView):
529529

530530
def get(self, request, *args, **kwargs):
531531
try:
532-
subscriptions = self.get_response_from_remote_api()
532+
subscriptions = self._get_response_from_remote_api()
533533
return Response(subscriptions, status=status.HTTP_200_OK)
534534
except requests.RequestException as e:
535535
return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
536536

537-
def get_link_to_remote_api(self) -> str:
537+
def _get_link_to_remote_api(self) -> str:
538538
# TODO something to reuse this code
539539
if settings.DEBUG:
540540
subscriptions_url = "https://skills.dev.procollab.ru/subscription/"
541541
else:
542542
subscriptions_url = "https://api.skills.procollab.ru/subscription/"
543543
return subscriptions_url
544544

545-
def get_response_from_remote_api(self):
546-
subscriptions_url = self.get_link_to_remote_api()
545+
def _get_response_from_remote_api(self):
546+
subscriptions_url = self._get_link_to_remote_api()
547547
response = requests.get(
548548
subscriptions_url,
549549
headers={
@@ -561,23 +561,23 @@ class RemoteCreatePayment(GenericAPIView):
561561

562562
def post(self, request, *args, **kwargs):
563563
try:
564-
subscriptions_buy_url = self.def_get_link_to_remote_api()
565-
data, headers = self.get_data_to_request_remote_api()
564+
subscriptions_buy_url = self._get_link_to_remote_api()
565+
data, headers = self._get_data_to_request_remote_api()
566566
response = requests.post(subscriptions_buy_url, json=data, headers=headers)
567567
response.raise_for_status()
568568
return Response(response.json(), status=status.HTTP_200_OK)
569569
except requests.RequestException as e:
570570
return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
571571

572-
def def_get_link_to_remote_api(self) -> str:
572+
def _get_link_to_remote_api(self) -> str:
573573
# TODO something to reuse this code
574574
if settings.DEBUG:
575575
subscriptions_buy_url = "https://skills.dev.procollab.ru/subscription/buy/"
576576
else:
577577
subscriptions_buy_url = "https://api.skills.procollab.ru/subscription/buy/"
578578
return subscriptions_buy_url
579579

580-
def get_data_to_request_remote_api(self) -> tuple[dict, dict]:
580+
def _get_data_to_request_remote_api(self) -> tuple[dict, dict]:
581581
serializer = self.serializer_class(data=self.request.data)
582582
if serializer.is_valid():
583583
data = serializer.validated_data
@@ -586,6 +586,7 @@ def get_data_to_request_remote_api(self) -> tuple[dict, dict]:
586586
"Authorization": self.request.META.get("HTTP_AUTHORIZATION"),
587587
}
588588
return data, headers
589+
589590
else:
590591
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
591592

@@ -597,10 +598,14 @@ def get(self, request, *args, **kwargs) -> HttpResponse:
597598
data_preparer = UserCVDataPreparer(request.user.pk)
598599
user_cv_data: UserCVData = data_preparer.get_prepared_data()
599600

600-
html_string: str = render_to_string('users/user_CV/user_CV_template.html', user_cv_data)
601+
html_string: str = render_to_string(
602+
"users/user_CV/user_CV_template.html", user_cv_data
603+
)
601604
binary_pdf_file: bytes | None = HTML(string=html_string).write_pdf()
602605

603-
encoded_filename: str = urllib.parse.quote(f"{request.user.first_name}_{request.user.last_name}.pdf")
606+
encoded_filename: str = urllib.parse.quote(
607+
f"{request.user.first_name}_{request.user.last_name}.pdf"
608+
)
604609
response = HttpResponse(binary_pdf_file, content_type="application/pdf")
605610
response["Content-Disposition"] = f'attachment; filename="{encoded_filename}"'
606611
return response

0 commit comments

Comments
 (0)