From dec526898e2fa32fd727484a7fbbf558fe3d912b Mon Sep 17 00:00:00 2001 From: lizerk Date: Tue, 25 Aug 2026 15:25:44 +0300 Subject: [PATCH] refactor: confirm skill rarity at fifty xp --- backend/employees/services/skill_progress.py | 5 +++-- backend/tests/api/v1/employees/test_skills.py | 20 +++++++++++++++++++ .../src/components/users/ProfileSections.tsx | 3 +++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/backend/employees/services/skill_progress.py b/backend/employees/services/skill_progress.py index 556c2aa0..df690dbd 100644 --- a/backend/employees/services/skill_progress.py +++ b/backend/employees/services/skill_progress.py @@ -29,6 +29,7 @@ SKILL_RANKS = ( ) ENDORSEMENT_COOLDOWN_DAYS = 90 +RARITY_CONFIRMATION_EXPERIENCE = 50 TASK_ASSIGNEE_SKILL_EXPERIENCE = 30 TASK_PARTICIPANT_SKILL_EXPERIENCE = 15 @@ -40,7 +41,7 @@ def skill_queryset_with_rarity(queryset=None): "employee_progresses", filter=Q( employee_progresses__is_active=True, - employee_progresses__experience__gte=100, + employee_progresses__experience__gte=RARITY_CONFIRMATION_EXPERIENCE, employee_progresses__employee__is_active=True, employee_progresses__employee__is_hidden_in_ui=False, ), @@ -108,7 +109,7 @@ def confirmed_holder_count(skill: Skill) -> int: EmployeeSkillProgress.objects.filter( skill=skill, is_active=True, - experience__gte=100, + experience__gte=RARITY_CONFIRMATION_EXPERIENCE, employee__is_active=True, employee__is_hidden_in_ui=False, employee__skills=skill, diff --git a/backend/tests/api/v1/employees/test_skills.py b/backend/tests/api/v1/employees/test_skills.py index c0c2492b..8c485516 100644 --- a/backend/tests/api/v1/employees/test_skills.py +++ b/backend/tests/api/v1/employees/test_skills.py @@ -11,9 +11,11 @@ from employees.models import ( ) from employees.services.skill_progress import ( award_task_skill_experience, + confirmed_holder_count, rank_payload, rarity_payload, revoke_task_skill_experience, + skill_queryset_with_rarity, ) from tasks.models import Task, TaskBoard, TaskColumn from tests.conftest import _unique_email, _unique_phone @@ -112,6 +114,24 @@ def test_skill_rank_and_rarity_boundaries(): assert rarity_payload(25)["rarity"] == "common" +@pytest.mark.django_db +def test_skill_rarity_counts_holders_from_fifty_experience(): + employee = _mk_user() + skill = Skill.objects.create(name="Redis") + employee.skills.add(skill) + progress = EmployeeSkillProgress.objects.get(employee=employee, skill=skill) + + progress.experience = 49 + progress.save(update_fields=["experience", "updated_at"]) + assert confirmed_holder_count(skill) == 0 + + progress.experience = 50 + progress.save(update_fields=["experience", "updated_at"]) + assert confirmed_holder_count(skill) == 1 + annotated_skill = skill_queryset_with_rarity(Skill.objects.filter(pk=skill.pk)).get() + assert annotated_skill.confirmed_holders == 1 + + @pytest.mark.django_db def test_adding_skill_creates_active_progress(api_client): actor = _mk_user() diff --git a/frontend/src/components/users/ProfileSections.tsx b/frontend/src/components/users/ProfileSections.tsx index f80dd9a3..20f83c23 100644 --- a/frontend/src/components/users/ProfileSections.tsx +++ b/frontend/src/components/users/ProfileSections.tsx @@ -1074,6 +1074,9 @@ export function ProfileSkillsInline({

Редкость относится к самому навыку и показывает, насколько редко он встречается среди подтверждённых обладателей в компании. Ранг при этом остаётся личным для каждого сотрудника.

+

+ Сотрудник считается подтверждённым обладателем навыка после достижения 50 XP — ранг «Бронза» для этого не требуется. +

{[ { rarity: "legendary" as const, label: "Легендарный · 1" },