Skip to content

Commit b7f47b8

Browse files
skills list api
ENT-4239
1 parent 433b3ef commit b7f47b8

File tree

9 files changed

+152
-1
lines changed

9 files changed

+152
-1
lines changed

taxonomy/api/__init__.py

Whitespace-only changes.

taxonomy/api/v1/__init__.py

Whitespace-only changes.

taxonomy/api/v1/serializers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from rest_framework import serializers
2+
from taxonomy.models import Skill
3+
4+
5+
class SkillSerializer(serializers.ModelSerializer):
6+
""" Skill Searlizer """
7+
8+
class Meta:
9+
model = Skill
10+
fields = ('name', 'description')

taxonomy/api/v1/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.urls import path
2+
from taxonomy.api.v1.views import SkillsView
3+
4+
urlpatterns = [
5+
path('api/v1/skills/', SkillsView.as_view(), name='skill_list')
6+
]

taxonomy/api/v1/views.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from rest_framework.generics import ListAPIView
2+
from rest_framework.permissions import IsAuthenticated
3+
from rest_framework import filters
4+
from taxonomy.models import Skill, CourseSkills
5+
from taxonomy.api.v1.serializers import SkillSerializer
6+
7+
8+
class SkillsView(ListAPIView):
9+
""" List view for Skills """
10+
filter_backends = [filters.SearchFilter]
11+
search_fields = ['^name']
12+
permission_classes = [IsAuthenticated]
13+
serializer_class = SkillSerializer
14+
15+
def get_queryset(self):
16+
blacklisted_skill_ids = list(
17+
CourseSkills.objects.filter(is_blacklisted=True).values_list('skill__id', flat=True)
18+
)
19+
return Skill.objects.exclude(id__in=blacklisted_skill_ids)
20+

taxonomy/urls.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
Taxonomy Connector URL Configuration.
44
"""
55

6-
from django.urls import re_path
6+
from django.urls import re_path, include
77

88
from taxonomy import views
9+
from taxonomy.api.v1.urls import urlpatterns as api_v1_urlpatterns
10+
911

1012
urlpatterns = [
1113
re_path(
1214
r"^admin/taxonomy/refresh_course_skills/$", views.RefreshCourseSkills.as_view(), name="refresh_course_skills"
1315
),
1416
]
17+
18+
urlpatterns += api_v1_urlpatterns

test_settings.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,18 @@ def root(*args):
3535
'django.contrib.sessions',
3636
'django.contrib.messages',
3737
'taxonomy',
38+
'rest_framework'
39+
)
40+
41+
MIDDLEWARE = (
42+
'django.contrib.sessions.middleware.SessionMiddleware',
43+
'django.middleware.locale.LocaleMiddleware',
44+
'django.middleware.common.CommonMiddleware',
45+
'django.middleware.csrf.CsrfViewMiddleware',
46+
'django.contrib.auth.middleware.AuthenticationMiddleware',
47+
'django.contrib.messages.middleware.MessageMiddleware',
48+
'django.contrib.sites.middleware.CurrentSiteMiddleware',
49+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
3850
)
3951

4052
LOCALE_PATHS = [
@@ -69,3 +81,9 @@ def root(*args):
6981
CELERY_BROKER_URL = 'memory://localhost/'
7082

7183
### END CELERY
84+
85+
REST_FRAMEWORK = {
86+
'DEFAULT_AUTHENTICATION_CLASSES': (
87+
'rest_framework.authentication.SessionAuthentication',
88+
),
89+
}

test_utils/factories.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import factory
66
from faker import Factory as FakerFactory
77

8+
from django.contrib.auth.models import User
9+
810
from taxonomy.models import CourseSkills, Job, JobPostings, JobSkills, Skill
911

1012
FAKER = FakerFactory.create()
@@ -94,3 +96,20 @@ class Meta:
9496
median_posting_duration = factory.LazyAttribute(lambda x: FAKER.pyint(min_value=0, max_value=100000000))
9597
unique_postings = factory.LazyAttribute(lambda x: FAKER.pyint(min_value=0, max_value=100000000))
9698
unique_companies = factory.LazyAttribute(lambda x: FAKER.pyint(min_value=0, max_value=100000000))
99+
100+
101+
class UserFactory(factory.django.DjangoModelFactory):
102+
"""
103+
Factory for User model.
104+
"""
105+
106+
class Meta:
107+
model = User
108+
109+
email = factory.LazyAttribute(lambda x: FAKER.email())
110+
username = factory.LazyAttribute(lambda x: FAKER.user_name())
111+
first_name = factory.LazyAttribute(lambda x: FAKER.first_name())
112+
last_name = factory.LazyAttribute(lambda x: FAKER.last_name())
113+
is_superuser = False
114+
is_staff = False
115+
is_active = True

tests/test_api_v1.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from pytest import mark
2+
from django.urls import reverse
3+
4+
from rest_framework.test import APITestCase
5+
from rest_framework import status
6+
from test_utils import factories
7+
8+
9+
TEST_USERNAME = 'taxonomy_user'
10+
TEST_EMAIL = '[email protected]'
11+
TEST_PASSWORD = 'password'
12+
13+
14+
@mark.django_db
15+
class TestSkillsView(APITestCase):
16+
def setUp(self):
17+
"""
18+
Perform operations common to all tests.
19+
"""
20+
super().setUp()
21+
self.user = self.create_user(username=TEST_USERNAME, email=TEST_EMAIL, password=TEST_PASSWORD)
22+
23+
self.url = reverse('skill_list')
24+
25+
self.whitelisted_skills = [
26+
'C Plus Plus', 'Command Line Interface', 'Data Strcutures', 'Biochemistry', 'Animations', 'Algorithms'
27+
]
28+
self.blacklisted_skills = ['Visual Basic', 'Oracle']
29+
30+
for skill_name in self.whitelisted_skills:
31+
skill = factories.SkillFactory(name=skill_name)
32+
factories.CourseSkillsFactory(skill=skill)
33+
34+
for skill_name in self.blacklisted_skills:
35+
skill = factories.SkillFactory(name=skill_name)
36+
factories.CourseSkillsFactory(skill=skill, is_blacklisted=True)
37+
38+
self.client.login(username=TEST_USERNAME, password=TEST_PASSWORD)
39+
40+
def create_user(self, username=TEST_USERNAME, password=TEST_PASSWORD, **kwargs):
41+
"""
42+
Create a test user and set its password.
43+
"""
44+
user = factories.UserFactory(username=username, **kwargs)
45+
user.set_password(password) # pylint: disable=no-member
46+
user.save() # pylint: disable=no-member
47+
return user
48+
49+
def test_search(self):
50+
"""
51+
Verify that skills endppoint return all skills when `search` query param is not given
52+
"""
53+
response = self.client.get(path=self.url)
54+
assert response.status_code == status.HTTP_200_OK
55+
skill_names = [skill['name'] for skill in response.json()]
56+
assert sorted(skill_names) == sorted(self.whitelisted_skills)
57+
58+
def test_search_with_query_param(self):
59+
"""
60+
Verify that skills endppoint return filtered skills according to the `search` query param
61+
"""
62+
response = self.client.get(path=self.url + '?search=algo')
63+
assert response.status_code == status.HTTP_200_OK
64+
skill_names = [skill['name'] for skill in response.json()]
65+
assert skill_names == ['Algorithms']
66+
67+
def test_search_with_blacklisted_skill(self):
68+
"""
69+
Verify that skills endppoint does not return blacklised skills.
70+
"""
71+
response = self.client.get(path=self.url + '?search=Oracle')
72+
assert response.status_code == status.HTTP_200_OK
73+
skill_names = [skill['name'] for skill in response.json()]
74+
assert skill_names == []

0 commit comments

Comments
 (0)