Skip to content

Commit

Permalink
Merge branch 'develop' into ft-articles-161255358
Browse files Browse the repository at this point in the history
  • Loading branch information
meshnesh committed Nov 8, 2018
2 parents 892cb80 + b6ca498 commit 54e3db0
Show file tree
Hide file tree
Showing 24 changed files with 280 additions and 5 deletions.
Binary file added .DS_Store
Binary file not shown.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,7 @@ designs/
# SQLite3
db.sqlite3

# Ignore articles/renderers for now
authors/apps/articles/renderers.py
# Admin
admin.py

.DS_Store
Binary file added authors/.DS_Store
Binary file not shown.
Binary file added authors/apps/.DS_Store
Binary file not shown.
Binary file added authors/apps/authentication/.DS_Store
Binary file not shown.
7 changes: 6 additions & 1 deletion authors/apps/authentication/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@

from .models import User
from authors.settings import SECRET_KEY
from authors.apps.profiles.models import Profile

from .backends import JWTAuthentication

from authors.apps.profiles.models import Profile

class RegistrationSerializer(serializers.ModelSerializer):
"""Serializers registration requests and creates a new user."""
Expand Down Expand Up @@ -55,7 +58,9 @@ class Meta:

def create(self, validated_data):
# Use the `create_user` method we wrote earlier to create a new user.
return User.objects.create_user(**validated_data)
user = User.objects.create_user(**validated_data)
Profile.objects.create(user=user)
return user


class LoginSerializer(serializers.Serializer):
Expand Down
2 changes: 0 additions & 2 deletions authors/apps/authentication/tests/test_reset_password.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ def test_invalid_email(self):
'''Test invalid email'''
response = self.client.post(self.FORGOT_URL, {"email": "qw"}, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(json.loads(response.content)["email"][0], "Enter a valid email address.")

def test_unregistered_user(self):
'''Test unregister user'''
Expand Down Expand Up @@ -104,4 +103,3 @@ def test_reset_password(self):
response13 = self.client.post(RESET_URL, {"new_password": "secret123", "confirm_password": "secret123"}, format="json")
self.assertEqual(response13.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(json.loads(response13.content)["error"][0], "You either have an invalid token or the token has expired.")

10 changes: 10 additions & 0 deletions authors/apps/core/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.db import models


class TimeStamp(models.Model):
"""Class that sets the timestamp when the an object is created and modified"""
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)

class Meta:
abstract = True
Binary file added authors/apps/profiles/.DS_Store
Binary file not shown.
5 changes: 5 additions & 0 deletions authors/apps/profiles/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class ProfilesConfig(AppConfig):
name = 'profiles'
6 changes: 6 additions & 0 deletions authors/apps/profiles/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from rest_framework.exceptions import APIException


class ProfileDoesNOtExist(APIException):
status_code = 400
default_detail = 'The requested profile does not exist.'
33 changes: 33 additions & 0 deletions authors/apps/profiles/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('surname', models.TextField(blank=True)),
('last_name', models.TextField(blank=True)),
('avatar', models.URLField(blank=True)),
('bio', models.TextField(blank=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('modified_at', models.DateTimeField(auto_now=True)),
('followers', models.IntegerField(default=0)),
('following', models.IntegerField(default=0)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ['-created_at', '-modified_at'],
'abstract': False
},
),
]
17 changes: 17 additions & 0 deletions authors/apps/profiles/migrations/0002_auto_20181104_1033.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 2.1.2 on 2018-11-04 10:33

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('profiles', '0001_initial'),
]

operations = [
migrations.AlterModelOptions(
name='profile',
options={},
),
]
21 changes: 21 additions & 0 deletions authors/apps/profiles/migrations/0003_auto_20181105_0939.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 2.1.2 on 2018-11-05 09:39

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('profiles', '0002_auto_20181104_1033'),
]

operations = [
migrations.RemoveField(
model_name='profile',
name='followers',
),
migrations.RemoveField(
model_name='profile',
name='following',
),
]
Empty file.
16 changes: 16 additions & 0 deletions authors/apps/profiles/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.db import models

from authors.apps.authentication.models import User
from authors.apps.core.models import TimeStamp


class Profile(TimeStamp):
"""Class description of user profile"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
surname = models.TextField(blank=True)
last_name = models.TextField(blank=True)
avatar = models.URLField(blank=True)
bio = models.TextField(blank=True)

def __str__(self):
return self.user.username
25 changes: 25 additions & 0 deletions authors/apps/profiles/renderers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import json

from rest_framework.renderers import JSONRenderer
from rest_framework.utils.serializer_helpers import ReturnList, ReturnDict


class ProfileJSONRenderer(JSONRenderer):

charset = 'utf-8'

def render(self, data, media_type=None, renderer_context=None):

if type(data) != ReturnList:
errors = data.get('errors', None)
if errors is not None:
return super().render(data)

if type(data) != ReturnDict:
return json.dumps({
'profile': data
})
else:
return json.dumps({
'profile': data
})
17 changes: 17 additions & 0 deletions authors/apps/profiles/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from rest_framework import serializers

from .models import Profile


class ProfileSerializer(serializers.ModelSerializer):
"""Serializer for creating a profile"""
username = serializers.CharField(source='user.username')
surname = serializers.CharField(allow_blank=True, required=False, min_length=1, max_length=50)
last_name = serializers.CharField(allow_blank=True, required=False, min_length=1, max_length=50)
avatar = serializers.URLField()
bio = serializers.CharField(allow_blank=True, required=False, min_length=5, max_length=255)

class Meta:
model = Profile
fields = ['username', 'surname', 'last_name', 'avatar', 'bio', 'created_at',
'modified_at']
Empty file.
26 changes: 26 additions & 0 deletions authors/apps/profiles/tests/base_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import json

from rest_framework.test import APIClient
from django.test import TestCase


class BaseTest(TestCase):

def setUp(self):
self.user_data = {
"username": "mathias",
"email": "mathias@gmail.com",
"password": "mathias92"
}

self.login_data = {
"email": "mathias@gmail.com",
"password": "mathias92"
}

self.client = APIClient()

self.register_response = self.client.post("/api/users/", self.user_data, format="json")
self.login_response = self.client.post("/api/users/login/", self.login_data, format="json")
token = json.loads(self.login_response.content)['user']['token']
self.client.credentials(HTTP_AUTHORIZATION="Bearer " + token)
30 changes: 30 additions & 0 deletions authors/apps/profiles/tests/test_profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from rest_framework import status

from authors.apps.profiles.tests.base_test import BaseTest


class ProfilesTest(BaseTest):

def test_list_profiles(self):
response = self.client.get('/api/profiles/', format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_user_view_their_profile(self):
response = self.client.get('/api/profiles/mathias', format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_retrieve_non_existing_profile(self):
response = self.client.get('/api/profiles/angule', format="json")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_update_profile(self):
data = {
"username": "mathias",
"surname": "code",
"last_name": "duty",
"avatar": "https://pbs.twimg.com/profile_images/670856248678596608/2yr7o6QQ_400x400.jpg",
"bio": "codeofdutycodeofdutycodeofduty"
}
response = self.client.put('/api/profiles/mathias', data, format="json")
self.assertIn("codeofdutycodeofdutycodeofduty", response.data['bio'])
self.assertEqual(response.status_code, status.HTTP_200_OK)
10 changes: 10 additions & 0 deletions authors/apps/profiles/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.urls import path

from .views import ProfileRetrieveUpdateAPIView, ProfileList

app_name = 'profiles'

urlpatterns = [
path('profiles/<username>', ProfileRetrieveUpdateAPIView.as_view(), name='profile'),
path('profiles/', ProfileList.as_view(), name='profiles'),
]
53 changes: 53 additions & 0 deletions authors/apps/profiles/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from rest_framework.generics import RetrieveUpdateAPIView, ListAPIView
from rest_framework.permissions import IsAuthenticated

from rest_framework.response import Response
from rest_framework import status

from django.shortcuts import get_object_or_404

from authors.apps.profiles.renderers import ProfileJSONRenderer
from authors.apps.profiles.serializers import ProfileSerializer
from authors.apps.profiles.models import User, Profile


class ProfileRetrieveUpdateAPIView(RetrieveUpdateAPIView):
"""
Users are able to edit their profile information
"""
permission_classes = (IsAuthenticated,)
renderer_classes = (ProfileJSONRenderer,)
serializer_class = ProfileSerializer

def retrieve(self, request, username, *args, **kwargs):
user = get_object_or_404(User, username=username)
serializer = self.serializer_class(user.profile)
return Response(serializer.data, status=status.HTTP_200_OK)

def update(self, request, username, *args, **kwargs):
serializer_data = request.data
user = get_object_or_404(User, username=username)
serializer_data = {
'surname': serializer_data.get('surname', request.user.profile.surname),
'last_name': serializer_data.get('last_name', request.user.profile.last_name),
'avatar': serializer_data.get('avatar', request.user.profile.avatar),
'bio': serializer_data.get('bio', request.user.profile.bio),
}

serializer = self.serializer_class(
request.user.profile,
data=serializer_data,
partial=True
)

serializer.is_valid(raise_exception=True)
serializer.update(request.user.profile, serializer_data)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)


class ProfileList(ListAPIView):
"""View all created profiles"""
permission_classes = (IsAuthenticated,)
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
1 change: 1 addition & 0 deletions authors/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@
path('api/', include(('authors.apps.authentication.urls',
'authentication'), namespace='authentication')),
path('api/', include('authors.apps.articles.urls')),
path('api/', include(('authors.apps.profiles.urls', 'profiles'), namespace='profiles')),
]

0 comments on commit 54e3db0

Please sign in to comment.