Skip to content

Commit 50a6e07

Browse files
committed
Merge branch 'dev'
2 parents 5279eb3 + 36ce91c commit 50a6e07

8 files changed

Lines changed: 198 additions & 89 deletions

File tree

.github/workflows/django-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ jobs:
1111
django-test:
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@v2
14+
- uses: actions/checkout@v3
1515

1616
- name: Set up Python 3.11
17-
uses: actions/setup-python@v2
17+
uses: actions/setup-python@v4
1818
with:
1919
python-version: 3.11
2020

.github/workflows/release-ci.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ jobs:
1010
name: 'Test before deploy'
1111
runs-on: ubuntu-latest
1212
steps:
13-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v3
1414

1515
- name: Set up Python 3.11
16-
uses: actions/setup-python@v2
16+
uses: actions/setup-python@v4
1717
with:
1818
python-version: 3.11
1919

@@ -52,24 +52,24 @@ jobs:
5252
needs: [ test ]
5353
steps:
5454
- name: "Checkout repository"
55-
uses: actions/checkout@v2
55+
uses: actions/checkout@v3
5656

5757
- name: "Set up QEMU"
58-
uses: docker/setup-qemu-action@v1
58+
uses: docker/setup-qemu-action@v3
5959

6060
- name: "Set up Docker Buildx"
61-
uses: docker/setup-buildx-action@v1
61+
uses: docker/setup-buildx-action@v3
6262

6363
- name: "Login to GitHub Registry"
64-
uses: docker/login-action@v1
64+
uses: docker/login-action@v3
6565
with:
6666
registry: ghcr.io
6767
username: ${{ github.actor }}
6868
password: ${{ secrets.GITHUB_TOKEN }}
6969

7070
- name: Docker meta
7171
id: meta
72-
uses: docker/metadata-action@v4
72+
uses: docker/metadata-action@v5
7373
with:
7474
images: ghcr.io/procollab-github/api
7575
flavor: latest=true
@@ -78,7 +78,7 @@ jobs:
7878
type=ref,event=pr
7979
type=semver,pattern={{version}}
8080
- name: Build and push container
81-
uses: docker/build-push-action@v3
81+
uses: docker/build-push-action@v5
8282
with:
8383
context: .
8484
file: Dockerfile

procollab/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@
118118
MIDDLEWARE = [
119119
"django_prometheus.middleware.PrometheusBeforeMiddleware",
120120
"django.middleware.security.SecurityMiddleware",
121+
"corsheaders.middleware.CorsMiddleware",
121122
"whitenoise.middleware.WhiteNoiseMiddleware",
122123
"django.contrib.sessions.middleware.SessionMiddleware",
123-
"corsheaders.middleware.CorsMiddleware",
124124
"django.middleware.common.CommonMiddleware",
125125
"django.middleware.csrf.CsrfViewMiddleware",
126126
"django.contrib.auth.middleware.AuthenticationMiddleware",

users/admin.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
1+
import urllib.parse
12
from datetime import date
23

34
import tablib
4-
import urllib.parse
55
from django.conf import settings
66
from django.contrib import admin
77
from django.contrib.auth.models import Permission
88
from django.http import HttpResponse
99
from django.shortcuts import redirect
1010
from django.urls import path
11+
from django.utils.timezone import now
1112

13+
from core.admin import SkillToObjectInline
1214
from core.utils import XlsxFileToExport
1315
from mailing.views import MailingTemplateRender
1416
from users.services.users_activity import UserActivityDataPreparer
15-
from .helpers import send_verification_completed_email, force_verify_user
17+
18+
from .helpers import force_verify_user, send_verification_completed_email
1619
from .models import (
1720
CustomUser,
18-
UserAchievement,
19-
Member,
20-
Mentor,
2121
Expert,
2222
Investor,
23-
UserLink,
23+
Member,
24+
Mentor,
25+
UserAchievement,
2426
UserEducation,
25-
UserWorkExperience,
26-
UserSkillConfirmation,
2727
UserLanguages,
28+
UserLink,
29+
UserSkillConfirmation,
30+
UserWorkExperience,
2831
)
2932

30-
from core.admin import SkillToObjectInline
31-
3233
admin.site.register(Permission)
3334

3435

@@ -55,7 +56,7 @@ class UserLanguagesInline(admin.TabularInline):
5556

5657
@admin.action(description="Сделать выбранных пользователей подтверждёнными")
5758
def make_active(modeladmin, request, queryset):
58-
queryset.update(is_active=True)
59+
queryset.update(is_active=True, verification_date=now().date())
5960

6061

6162
@admin.register(CustomUser)
@@ -268,7 +269,9 @@ def get_users_activity(self, _) -> HttpResponse:
268269
binary_data_to_export,
269270
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
270271
)
271-
response["Content-Disposition"] = f'attachment; filename*=UTF-8\'\'{encoded_file_name}'
272+
response["Content-Disposition"] = (
273+
f"attachment; filename*=UTF-8''{encoded_file_name}"
274+
)
272275

273276
return response
274277

users/helpers.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from django.conf import settings
2-
from django.core.cache import cache
3-
from django.utils import timezone
42
from django.contrib.auth import get_user_model
5-
from rest_framework_simplejwt.tokens import RefreshToken
63
from django.contrib.sites.shortcuts import get_current_site
4+
from django.core.cache import cache
75
from django.urls import reverse
6+
from django.utils import timezone
7+
from django.utils.timezone import now
8+
from rest_framework_simplejwt.tokens import RefreshToken
89

910
from mailing.utils import send_mail
1011
from users.constants import PROTOCOL
@@ -31,7 +32,8 @@ def verify_email(user, request):
3132

3233
def send_verification_completed_email(user: User):
3334
template_content = open(
34-
settings.BASE_DIR / "templates/email/verification-succeed.html", encoding="utf-8"
35+
settings.BASE_DIR / "templates/email/verification-succeed.html",
36+
encoding="utf-8",
3537
).read()
3638
send_mail(
3739
user=user,
@@ -97,6 +99,8 @@ def force_verify_user(user: User) -> None:
9799

98100
# todo: send email
99101
user.is_active = True
102+
if hasattr(user, "verification_date"):
103+
user.verification_date = now().date()
100104
user.save()
101105

102106

users/serializers.py

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ def to_representation(self, data):
4747
if isinstance(data, list):
4848
return data
4949
return [
50-
i.replace("'", "") for i in data.strip("][").split(",") if i.replace("'", "")
50+
i.replace("'", "")
51+
for i in data.strip("][").split(",")
52+
if i.replace("'", "")
5153
]
5254

5355

@@ -107,6 +109,7 @@ class Meta:
107109

108110
class UserDataConfirmationSerializer(serializers.ModelSerializer):
109111
"""Information about the User to add to the skill confirmation information."""
112+
110113
v2_speciality = SpecializationSerializer()
111114

112115
class Meta:
@@ -148,12 +151,15 @@ def to_representation(self, instance):
148151
"""Returns correct data about user in `confirmed_by`."""
149152
data = super().to_representation(instance)
150153
data.pop("skill_to_object", None)
151-
data["confirmed_by"] = UserDataConfirmationSerializer(instance.confirmed_by).data
154+
data["confirmed_by"] = UserDataConfirmationSerializer(
155+
instance.confirmed_by
156+
).data
152157
return data
153158

154159

155160
class UserApproveSkillResponse(serializers.Serializer):
156161
"""For swagger response presentation."""
162+
157163
confirmed_by = UserDataConfirmationSerializer(read_only=True)
158164

159165

@@ -173,14 +179,14 @@ class Meta:
173179

174180
def get_approves(self, obj):
175181
"""Adds information about confirm to the skill."""
176-
confirmations = (
177-
UserSkillConfirmation.objects
178-
.filter(skill_to_object=obj)
179-
.select_related('confirmed_by')
180-
)
182+
confirmations = UserSkillConfirmation.objects.filter(
183+
skill_to_object=obj
184+
).select_related("confirmed_by")
181185
return [
182186
{
183-
"confirmed_by": UserDataConfirmationSerializer(confirmation.confirmed_by).data,
187+
"confirmed_by": UserDataConfirmationSerializer(
188+
confirmation.confirmed_by
189+
).data,
184190
}
185191
for confirmation in confirmations
186192
]
@@ -300,14 +306,15 @@ def validate(self, attrs):
300306
completion_year = attrs.get("completion_year")
301307
entry_year = attrs.get("entry_year")
302308
if (entry_year and completion_year) and (entry_year > completion_year):
303-
raise ValidationError({
304-
"entry_year": constants.USER_EXPERIENCE_YEAR_VALIDATION_MESSAGE,
305-
})
309+
raise ValidationError(
310+
{
311+
"entry_year": constants.USER_EXPERIENCE_YEAR_VALIDATION_MESSAGE,
312+
}
313+
)
306314
return attrs
307315

308316

309317
class UserEducationSerializer(UserExperienceMixin, serializers.ModelSerializer):
310-
311318
class Meta:
312319
model = UserEducation
313320
fields = [
@@ -321,7 +328,6 @@ class Meta:
321328

322329

323330
class UserWorkExperienceSerializer(UserExperienceMixin, serializers.ModelSerializer):
324-
325331
class Meta:
326332
model = UserWorkExperience
327333
fields = [
@@ -334,7 +340,6 @@ class Meta:
334340

335341

336342
class UserLanguagesSerializer(serializers.ModelSerializer):
337-
338343
class Meta:
339344
model = UserLanguages
340345
fields = [
@@ -391,11 +396,9 @@ def get_projects(self, user: CustomUser):
391396
).data
392397

393398
def get_programs(self, user: CustomUser):
394-
user_program_profiles = (
395-
user.partner_program_profiles
396-
.select_related('partner_program')
397-
.filter(partner_program__draft=False)
398-
)
399+
user_program_profiles = user.partner_program_profiles.select_related(
400+
"partner_program"
401+
).filter(partner_program__draft=False)
399402
return UserProgramsSerializer(
400403
[profile.partner_program for profile in user_program_profiles],
401404
context={"request": self.context.get("request"), "user": user},
@@ -523,7 +526,10 @@ def update(self, instance, validated_data):
523526
if attr in IMMUTABLE_FIELDS + USER_TYPE_FIELDS + RELATED_FIELDS:
524527
continue
525528
if attr == "user_type":
526-
if value == instance.user_type or value not in user_types_to_attr.keys():
529+
if (
530+
value == instance.user_type
531+
or value not in user_types_to_attr.keys()
532+
):
527533
continue
528534
# we can't change user type to Member
529535
if value == CustomUser.MEMBER:
@@ -556,13 +562,17 @@ def _update_user_education(self, instance: CustomUser, data: list[dict]) -> None
556562
serializer.save(user=instance)
557563

558564
@transaction.atomic
559-
def _update_user_work_experience(self, instance: CustomUser, data: list[dict]) -> None:
565+
def _update_user_work_experience(
566+
self, instance: CustomUser, data: list[dict]
567+
) -> None:
560568
"""
561569
Update user work experience.
562570
`PUT`/ `PATCH` methods require full data about education.
563571
"""
564572
instance.work_experience.all().delete()
565-
serializer = UserWorkExperienceSerializer(data=data, many=True, context=self.context)
573+
serializer = UserWorkExperienceSerializer(
574+
data=data, many=True, context=self.context
575+
)
566576
if serializer.is_valid(raise_exception=True):
567577
serializer.save(user=instance)
568578

@@ -575,7 +585,9 @@ def _update_user_languages(self, instance: CustomUser, data: list[dict]) -> None
575585
# Only unique languages in profile.
576586
languages = [lang_data["language"] for lang_data in data]
577587
if len(languages) != len(set(languages)):
578-
raise ValidationError({"language": constants.UNIQUE_LANGUAGES_VALIDATION_MESSAGE})
588+
raise ValidationError(
589+
{"language": constants.UNIQUE_LANGUAGES_VALIDATION_MESSAGE}
590+
)
579591
# Custom validation to limit the number of languages per user to `USER_MAX_LANGUAGES_COUNT`.
580592
if len(languages) > constants.USER_MAX_LANGUAGES_COUNT:
581593
raise ValidationError(constants.COUNT_LANGUAGES_VALIDATION_MESSAGE)
@@ -591,7 +603,9 @@ def _update_user_skills(self, instance: CustomUser, data: list[int]) -> None:
591603
Required count of skills between 1 and `USER_MAX_SKILL_QUANTITY`.
592604
"""
593605
if not (1 <= len(data) <= constants.USER_MAX_SKILL_QUANTITY):
594-
raise serializers.ValidationError(constants.USER_SKILL_QUANTITY_VALIDATIONS_MESSAGE)
606+
raise serializers.ValidationError(
607+
constants.USER_SKILL_QUANTITY_VALIDATIONS_MESSAGE
608+
)
595609

596610
user_content_type = ContentType.objects.get_for_model(CustomUser)
597611

@@ -624,7 +638,9 @@ def _update_user_skills(self, instance: CustomUser, data: list[int]) -> None:
624638

625639
def _user_skills_quantity_limit_validation(self, instance: CustomUser) -> None:
626640
if instance.skills_count > constants.USER_MAX_SKILL_QUANTITY:
627-
raise serializers.ValidationError(constants.USER_SKILL_QUANTITY_VALIDATIONS_MESSAGE)
641+
raise serializers.ValidationError(
642+
constants.USER_SKILL_QUANTITY_VALIDATIONS_MESSAGE
643+
)
628644

629645
def to_representation(self, instance) -> dict[str, Any]:
630646
"""
@@ -734,6 +750,50 @@ class Meta:
734750
}
735751

736752

753+
class PublicUserSerializer(serializers.ModelSerializer):
754+
firstName = serializers.CharField(source="first_name")
755+
lastName = serializers.CharField(source="last_name")
756+
skills = serializers.SerializerMethodField()
757+
is_online = serializers.SerializerMethodField()
758+
759+
def get_skills(self, user: CustomUser) -> list:
760+
"""Возвращает список навыков без поля approves"""
761+
skills = []
762+
for sto in getattr(user, "prefetched_skills", []):
763+
skill = sto.skill
764+
skills.append(
765+
{
766+
"id": skill.id,
767+
"name": skill.name,
768+
"category": {"id": skill.category.id, "name": skill.category.name},
769+
}
770+
)
771+
return skills
772+
773+
def get_is_online(self, user: CustomUser) -> bool:
774+
"""Логика проверки онлайн-статуса"""
775+
request = self.context.get("request")
776+
if request and request.user.is_authenticated and request.user.id == user.id:
777+
return True
778+
779+
cache_key = get_user_online_cache_key(user)
780+
return cache.get(cache_key, False)
781+
782+
class Meta:
783+
model = CustomUser
784+
fields = [
785+
"id",
786+
"firstName",
787+
"lastName",
788+
"avatar",
789+
"user_type",
790+
"skills",
791+
"is_online",
792+
"birthday",
793+
"speciality",
794+
]
795+
796+
737797
class UserFeedSerializer(serializers.ModelSerializer, SkillsSerializerMixin):
738798
class Meta:
739799
model = CustomUser

0 commit comments

Comments
 (0)