Skip to content

Commit

Permalink
Merge 46a5c33 into 7e99263
Browse files Browse the repository at this point in the history
  • Loading branch information
TheSteelGuy committed Nov 12, 2018
2 parents 7e99263 + 46a5c33 commit 8f1b04a
Show file tree
Hide file tree
Showing 63 changed files with 236 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,4 @@ db.sqlite3
!*/migrations/__init__.py
.vscode/
*.DS_Store
*migrations
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ script:
- python3 manage.py migrate authentication
- python3 manage.py makemigrations article
- python3 manage.py migrate article
- python3 manage.py makemigrations profiles
- python3 manage.py makemigrations
- python3 manage.py migrate
- coverage run manage.py test
Expand Down
Binary file added article/example.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_033kK5t.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_07w0yYH.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_1iXBmMR.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_4Kef3eN.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_4OcfZLi.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_7H5yc0H.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_Ditfv77.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_Ix08VCf.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_Jto0Zsc.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_M3KOuoT.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_P9RkWCa.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_UW23hBe.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_XVxnDlT.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_YugPnAh.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_dK1f8wX.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_eYQuOq9.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_fAirp4p.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_ityEKgZ.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_jjFAcuu.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_mHUOOtE.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_ofs9xmg.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_pnfVCgm.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added article/example_xX3GuhB.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified authors/__init__.py
100644 → 100755
Empty file.
Empty file modified authors/apps/__init__.py
100644 → 100755
Empty file.
Empty file modified authors/apps/article/apps.py
100644 → 100755
Empty file.
Empty file modified authors/apps/article/tests/templates/activate_account.html
100644 → 100755
Empty file.
Empty file modified authors/apps/article/tests/test_rate_article.py
100644 → 100755
Empty file.
Empty file modified authors/apps/article/views.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/__init__.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/backends.py
100644 → 100755
Empty file.
Empty file.
Empty file modified authors/apps/authentication/models.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/renderers.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/serializers.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/signals.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/templates/activate_account.html
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/templates/alert_reset_password.html
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/templates/email_confirm.html
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/tests/__init__.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/tests/test_email_verification.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/tests/test_social_auth.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/tests/test_user.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/urls.py
100644 → 100755
Empty file.
Empty file modified authors/apps/authentication/views.py
100644 → 100755
Empty file.
Empty file modified authors/apps/core/__init__.py
100644 → 100755
Empty file.
Empty file modified authors/apps/core/exceptions.py
100644 → 100755
Empty file.
Empty file modified authors/apps/core/models.py
100644 → 100755
Empty file.
Empty file modified authors/apps/core/renderers.py
100644 → 100755
Empty file.
Empty file.
36 changes: 36 additions & 0 deletions authors/apps/profiles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,44 @@ class UserProfile(TimestampedModel):
bio = models.TextField(blank=True)
image = models.URLField(blank=True)

# many to many relationship, meaning both sides of the
# profile can have more than one follower
follows = models.ManyToManyField(
'self',
related_name='followed_by',
symmetrical=False
)

class Meta:
app_label = "profiles"

def __str__(self):
return self.user.username

def follow(self, my_profile):
"""
follow my profile if you are not following me yet
:param my_profile: profile to follow
:return: none
"""
self.follows.add(my_profile)

def unfollow(self, my_profile):
"""
quit following me if you are are already following me
:param my_profile: profile to unfollow
:return: none
"""
self.follows.remove(my_profile)

def is_following(self, profile):
"""
check if am following the other profile
:param profile: the other profile
:return: True if am following the other profile else False
"""
return self.follows.filter(pk=profile.pk).exists()

def is_followed_by(self, my_profile):
"""check if the the other profile is following my profile"""
return self.followed_by.filter(pk=my_profile.pk).exists()
15 changes: 14 additions & 1 deletion authors/apps/profiles/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ class ProfileSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
bio = serializers.CharField(allow_blank=True, required=False)
image = serializers.SerializerMethodField()
following = serializers.SerializerMethodField()

class Meta:
model = UserProfile
fields = ('username', 'bio', 'image',)
fields = ('username', 'bio', 'image', 'following',)
read_only_fields = ('username',)

def get_image(self, obj):
Expand All @@ -18,6 +19,18 @@ def get_image(self, obj):

return 'null'

def get_following(self, instance):
"""get the new profile instance which we want to follow"""
# get the request context which contains the user
request = self.context.get('request', None)
if request is None:
return False
if request.user.is_authenticated:
# obtain the profile instance from the request
follower = request.user.userprofile
followee = instance
return follower.is_following(followee)


class ProfileListSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username', read_only=True)
Expand Down
118 changes: 118 additions & 0 deletions authors/apps/profiles/tests/test_follow_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework.test import APITestCase
from rest_framework import status


class TestFollowUserProfile(APITestCase):
""" setup class for follow profile tests"""

def setUp(self):
"""
Prepare test environment for each testcase
"""
self.client = APIClient()
self.user_details = {
'user': {
'username': 'steel',
'email': 'steel@gmail.com',
'password': 'somepass12345',
}
}
self.follow_me = {
'user': {
'username': 'bond',
'email': 'bond@gmail.com',
'password': 'somepass12345',
}
}

self.login_data = {
"user": {
'email': 'steel@gmail.com',
'password': 'somepass12345'
}
}

self.login_url = reverse('authentication:login')
self.signup_url = reverse('authentication:register')
self.all_profiles = reverse('profile:all_profiles')
self.update_profile = reverse('authentication:update_profile')

def register_user(self, user_details):
"""
register a new user
"""
self.client.post(
self.signup_url,
user_details,
format='json')

def login_user(self):
"""login user to get the token"""
response = self.client.post(
self.login_url, self.login_data, format='json')
return response.data['token']

def test_follow_another_user_profile(self):
"""
test follow another profile
"""
self.register_user(self.user_details)
self.register_user(self.follow_me)
token = self.login_user()
response = self.client.post(
reverse(
"profile:follow",
args=['bond']),
format='json',
HTTP_AUTHORIZATION='Bearer ' +
token)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIn('True', str(response.data))

def test_un_follow_another_user_profile(self):
"""
test un follow another profile
"""
self.register_user(self.user_details)
self.register_user(self.follow_me)
token = self.login_user()
response = self.client.delete(
reverse(
"profile:follow",
args=['bond']),
format='json', HTTP_AUTHORIZATION='Bearer ' + token)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('False', str(response.data))

def test_follow_my_self(self):
"""
test follow myself
"""
self.register_user(self.user_details)
self.register_user(self.follow_me)
token = self.login_user()
response = self.client.post(
reverse(
"profile:follow",
args=['steel']),
format='json', HTTP_AUTHORIZATION='Bearer ' + token)
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
self.assertIn(
'You can only follow others, not yourself', str(response.data))

def test_follow_without_authentication(self):
"""
test follow another profile
"""
self.register_user(self.user_details)
self.register_user(self.follow_me)
response = self.client.post(
reverse(
"profile:follow",
args=['steel']),
format='json')
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertIn(
'Authentication credentials were not provided', str(response.data))
5 changes: 4 additions & 1 deletion authors/apps/profiles/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.urls import path

from authors.apps.profiles.views import ProfileListAPIView
from authors.apps.profiles.views import ProfileListAPIView,\
ProfileFollowAPIView
from .views import ProfileRetrieveAPIView

profiles = 'profiles/'
Expand All @@ -11,4 +12,6 @@
'profiles/<username>/',
ProfileRetrieveAPIView.as_view(),
name='profile'),
path('profiles/<username>/follow/',
ProfileFollowAPIView.as_view(), name='follow')
]
47 changes: 45 additions & 2 deletions authors/apps/profiles/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ def retrieve(self, request, username, *args, **kwargs):
},
status=status.HTTP_404_NOT_FOUND
)
serializer = self.serializer_class(profile)
serializer = self.serializer_class(profile, context={
'request': request
})

return Response(serializer.data, status=status.HTTP_200_OK)

Expand Down Expand Up @@ -75,7 +77,7 @@ def get(self, request, **kwargs):
"""
try:
queryset = UserProfile.objects.all()
except Exception:
except UserProfile.DoesNotExist:
return Response(
{
'Message': 'There are no profiles found'
Expand All @@ -86,3 +88,44 @@ def get(self, request, **kwargs):
profiles = Response({'profiles': serializer.data},
status=status.HTTP_200_OK)
return profiles


class ProfileFollowAPIView(APIView):
permission_classes = (IsAuthenticated,)
renderer_classes = (ProfileJSONRenderer,)
serializer_class = ProfileSerializer

def follow_unfollow(self, username, request, check, status_):
follower = self.request.user.userprofile
try:
followee = UserProfile.objects.get(user__username=username)
except UserProfile.DoesNotExist:
return Response(
{'Message': 'No profile with this username was found'},
status=status.HTTP_204_NO_CONTENT
)

if follower.pk is followee.pk:
return Response({
'Message': 'You can only follow others, not yourself'
}, status=status.HTTP_409_CONFLICT)

if check:
follower.follow(followee)
if not check:
follower.unfollow(followee)

serializer = self.serializer_class(followee, context={
'request': request
})
return Response(serializer.data, status=status_)

def post(self, request, username=None):
"""follow a profile with username 'username'"""
return self.follow_unfollow(
username, request, True, status.HTTP_201_CREATED)

def delete(self, request, username=None):
"""un-follow a profile """
return self.follow_unfollow(
username, request, False, status.HTTP_200_OK)
Empty file modified authors/settings.py
100644 → 100755
Empty file.
Empty file modified authors/urls.py
100644 → 100755
Empty file.
Empty file modified authors/wsgi.py
100644 → 100755
Empty file.
4 changes: 4 additions & 0 deletions release-tasks.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#!/usr/bin/env bash
python manage.py makemigrations authentication
python manage.py migrate authentication
python managy.py makemigrations article
python manage.py migrate article
python manage.py makemigrations profiles
python manage.py migrate profiles
python manage.py makemigrations
python manage.py migrate
16 changes: 13 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
asn1crypto==0.24.0
astroid==2.0.4
atomicwrites==1.2.1
attrs==18.2.0
autopep8==1.4.2
certifi==2018.10.15
cffi==1.11.5
chardet==3.0.4
colorama==0.3.9
coreapi==2.3.3
coreschema==0.0.4
coverage==4.5.1
cryptography==2.3.1
defusedxml==0.5.0
dj-database-url==0.5.0
Django==2.1.2
django-allauth==0.38.0
django-autoslug-iplweb==1.9.4
django-cors-headers==2.4.0
django-extensions==2.1.3
django-heroku==0.3.1
django-rest-swagger==2.1.0
django-versatileimagefield==1.10
djangorestframework==3.9.0
flake8==3.6.0
flake8-polyfill==1.0.2
factory-boy==2.11.1
Faker==0.9.2
gunicorn==19.9.0
Expand All @@ -25,32 +32,35 @@ isort==4.3.4
itypes==1.1.0
Jinja2==2.10
lazy-object-proxy==1.3.1
mando==0.6.4
MarkupSafe==1.0
mccabe==0.6.1
more-itertools==4.3.0
oauthlib==2.1.0
openapi-codec==1.3.2
Pillow==5.3.0
pluggy==0.8.0
psycopg2==2.7.5
psycopg2-binary==2.7.5
py==1.7.0
flake8==3.6.0
mccabe==0.6.1
psycopg2-binary==2.7.5
pycodestyle==2.4.0
pycparser==2.19
pyflakes==2.0.0
PyJWT==1.6.4
pylint==2.1.1
pytest==3.9.3
python-dateutil==2.7.5
python-http-client==3.1.0
python3-openid==3.1.0
pytz==2018.5
radon==2.4.0
requests==2.20.0
requests-oauthlib==1.0.0
simplejson==3.16.0
six==1.11.0
social-auth-app-django==3.1.0
social-auth-core==2.0.0
text-unidecode==1.2
uritemplate==3.0.0
urllib3==1.24
whitenoise==4.1
Expand Down

0 comments on commit 8f1b04a

Please sign in to comment.