Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP --- skills list api #50

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added taxonomy/api/__init__.py
Empty file.
Empty file added taxonomy/api/v1/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions taxonomy/api/v1/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from rest_framework import serializers
from taxonomy.models import Skill


class SkillSerializer(serializers.ModelSerializer):
""" Skill Searlizer """

class Meta:
model = Skill
fields = ('name', 'description')
6 changes: 6 additions & 0 deletions taxonomy/api/v1/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.urls import path
from taxonomy.api.v1.views import SkillsView

urlpatterns = [
path('api/v1/skills/', SkillsView.as_view(), name='skill_list')
]
19 changes: 19 additions & 0 deletions taxonomy/api/v1/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from rest_framework.generics import ListAPIView
from rest_framework.permissions import IsAuthenticated
from taxonomy.models import Skill
from taxonomy.api.v1.serializers import SkillSerializer


class SkillsView(ListAPIView):
""" List view for Skills """
permission_classes = [IsAuthenticated]
serializer_class = SkillSerializer

def get_queryset(self):
search = self.request.query_params.get('search')

queryset = Skill.objects.exclude(courseskills__is_blacklisted=True)
if search:
queryset = queryset.filter(name__icontains=search)

return queryset
18 changes: 18 additions & 0 deletions taxonomy/migrations/0010_auto_20210305_0914.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.2.17 on 2021-03-05 09:14

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('taxonomy', '0009_skill_description'),
]

operations = [
migrations.AlterField(
model_name='skill',
name='name',
field=models.CharField(blank=True, db_index=True, help_text='The name of the skill.', max_length=255),
),
]
1 change: 1 addition & 0 deletions taxonomy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Skill(TimeStampedModel):
name = models.CharField(
max_length=255,
blank=True,
db_index=True,
help_text=_(
'The name of the skill.'
)
Expand Down
6 changes: 5 additions & 1 deletion taxonomy/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
Taxonomy Connector URL Configuration.
"""

from django.urls import re_path
from django.urls import re_path, include

from taxonomy import views
from taxonomy.api.v1.urls import urlpatterns as api_v1_urlpatterns


urlpatterns = [
re_path(
r"^admin/taxonomy/refresh_course_skills/$", views.RefreshCourseSkills.as_view(), name="refresh_course_skills"
),
]

urlpatterns += api_v1_urlpatterns
18 changes: 18 additions & 0 deletions test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ def root(*args):
'django.contrib.sessions',
'django.contrib.messages',
'taxonomy',
'rest_framework'
)

MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.sites.middleware.CurrentSiteMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

LOCALE_PATHS = [
Expand Down Expand Up @@ -69,3 +81,9 @@ def root(*args):
CELERY_BROKER_URL = 'memory://localhost/'

### END CELERY

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
}
19 changes: 19 additions & 0 deletions test_utils/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import factory
from faker import Factory as FakerFactory

from django.contrib.auth.models import User

from taxonomy.models import CourseSkills, Job, JobPostings, JobSkills, Skill

FAKER = FakerFactory.create()
Expand Down Expand Up @@ -94,3 +96,20 @@ class Meta:
median_posting_duration = factory.LazyAttribute(lambda x: FAKER.pyint(min_value=0, max_value=100000000))
unique_postings = factory.LazyAttribute(lambda x: FAKER.pyint(min_value=0, max_value=100000000))
unique_companies = factory.LazyAttribute(lambda x: FAKER.pyint(min_value=0, max_value=100000000))


class UserFactory(factory.django.DjangoModelFactory):
"""
Factory for User model.
"""

class Meta:
model = User

email = factory.LazyAttribute(lambda x: FAKER.email())
username = factory.LazyAttribute(lambda x: FAKER.user_name())
first_name = factory.LazyAttribute(lambda x: FAKER.first_name())
last_name = factory.LazyAttribute(lambda x: FAKER.last_name())
is_superuser = False
is_staff = False
is_active = True
75 changes: 75 additions & 0 deletions tests/test_api_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from pytest import mark
from django.urls import reverse

from rest_framework.test import APITestCase
from rest_framework import status
from test_utils import factories


TEST_USERNAME = 'taxonomy_user'
TEST_EMAIL = '[email protected]'
TEST_PASSWORD = 'password'


@mark.django_db
class TestSkillsView(APITestCase):
def setUp(self):
"""
Perform operations common to all tests.
"""
super().setUp()
self.user = self.create_user(username=TEST_USERNAME, email=TEST_EMAIL, password=TEST_PASSWORD)

self.url = reverse('skill_list')

self.whitelisted_skills = [
'C Plus Plus', 'Command Line Interface', 'Data Structures', 'Biochemistry',
'Animations', 'Algorithms', 'Data Science', 'Data Wrangling', 'Databases'
]
self.blacklisted_skills = ['Visual Basic', 'Oracle']

for skill_name in self.whitelisted_skills:
skill = factories.SkillFactory(name=skill_name)
factories.CourseSkillsFactory(skill=skill)

for skill_name in self.blacklisted_skills:
skill = factories.SkillFactory(name=skill_name)
factories.CourseSkillsFactory(skill=skill, is_blacklisted=True)

self.client.login(username=TEST_USERNAME, password=TEST_PASSWORD)

def create_user(self, username=TEST_USERNAME, password=TEST_PASSWORD, **kwargs):
"""
Create a test user and set its password.
"""
user = factories.UserFactory(username=username, **kwargs)
user.set_password(password) # pylint: disable=no-member
user.save() # pylint: disable=no-member
return user

def test_search(self):
"""
Verify that skills endppoint return all skills when `search` query param is not given
"""
response = self.client.get(path=self.url)
assert response.status_code == status.HTTP_200_OK
skill_names = [skill['name'] for skill in response.json()]
assert sorted(skill_names) == sorted(self.whitelisted_skills)

def test_search_with_query_param(self):
"""
Verify that skills endppoint return filtered skills according to the `search` query param
"""
response = self.client.get(path=self.url + '?search=data')
assert response.status_code == status.HTTP_200_OK
skill_names = [skill['name'] for skill in response.json()]
assert skill_names == ['Data Structures', 'Data Science', 'Data Wrangling', 'Databases' ]

def test_search_with_blacklisted_skill(self):
"""
Verify that skills endppoint does not return blacklised skills.
"""
response = self.client.get(path=self.url + '?search=Oracle')
assert response.status_code == status.HTTP_200_OK
skill_names = [skill['name'] for skill in response.json()]
assert skill_names == []