diff --git a/VoxPopLoader/loader.py b/VoxPopLoader/loader.py index e98c5b8..2422e90 100644 --- a/VoxPopLoader/loader.py +++ b/VoxPopLoader/loader.py @@ -412,7 +412,9 @@ def __get_propositions(self): # Situação 'situation': proposition_result[sp]['descricaoSituacao'], # URL da proposição na íntegra - 'url_full': proposition_result[sp]['url'] + 'url_full': proposition_result[sp]['url'], + # Última atualização da proposição + 'last_update': proposition_result[sp]['dataHora'] } requests.post( diff --git a/api/admin.py b/api/admin.py index 8c38f3f..bff47e1 100644 --- a/api/admin.py +++ b/api/admin.py @@ -1,3 +1,91 @@ +from .models import ( + ExtendedUser, Parliamentary, ParliamentaryVote, Proposition, + SocialInformation, UserFollowing, UserVote, ContactUs +) from django.contrib import admin -# Register your models here. + +class ExtendedUserAdmin(admin.ModelAdmin): + list_display = [ + 'user', + 'should_update' + ] + + +class ParliamentaryAdmin(admin.ModelAdmin): + list_display = [ + 'name', + 'gender', + 'political_party', + 'federal_unit', + 'birth_date', + 'education', + 'email' + ] + + +class ParliamentaryVoteAdmin(admin.ModelAdmin): + list_display = [ + 'option', + 'proposition', + 'parliamentary' + ] + + +class PropositionAdmin(admin.ModelAdmin): + list_display = [ + 'proposition_type', + 'proposition_type_initials', + 'number', + 'year', + 'abstract', + 'processing', + 'situation', + 'last_update' + ] + + +class SocialInformationAdmin(admin.ModelAdmin): + list_display = [ + 'owner', + 'region', + 'income', + 'education', + 'race', + 'gender', + 'birth_date', + ] + + +class UserFollowingAdmin(admin.ModelAdmin): + list_display = [ + 'user', + 'parliamentary' + ] + + +class UserVoteAdmin(admin.ModelAdmin): + list_display = [ + 'option', + 'proposition', + 'user' + ] + + +class ContactUsAdmin(admin.ModelAdmin): + list_display = [ + 'topic', + 'email', + 'choice', + 'text' + ] + + +admin.site.register(ExtendedUser, ExtendedUserAdmin) +admin.site.register(Parliamentary, ParliamentaryAdmin) +admin.site.register(ParliamentaryVote, ParliamentaryVoteAdmin) +admin.site.register(Proposition, PropositionAdmin) +admin.site.register(SocialInformation, SocialInformationAdmin) +admin.site.register(UserFollowing, UserFollowingAdmin) +admin.site.register(UserVote, UserVoteAdmin) +admin.site.register(ContactUs, ContactUsAdmin) diff --git a/api/models.py b/api/models.py index 2aae9fa..89e5fd1 100644 --- a/api/models.py +++ b/api/models.py @@ -5,48 +5,47 @@ # Create your models here. -UF_CHOICES = ( - ('N', 'Null'), - ('AC', 'Acre'), - ('AL', 'Alagoas'), - ('AP', 'Amapá'), - ('BA', 'Bahia'), - ('CE', 'Ceará'), - ('DF', 'Distrito Federal'), - ('ES', 'Espírito Santo'), - ('GO', 'Goiás'), - ('MA', 'Maranão'), - ('MG', 'Minas Gerais'), - ('MS', 'Mato Grosso do Sul'), - ('MT', 'Mato Grosso'), - ('PA', 'Pará'), - ('PB', 'Paraíba'), - ('PE', 'Pernanbuco'), - ('PI', 'Piauí'), - ('PR', 'Paraná'), - ('RJ', 'Rio de Janeiro'), - ('RN', 'Rio Grande do Norte'), - ('RO', 'Rondônia'), - ('RR', 'Roraima'), - ('RS', 'Rio Grande do Sul'), - ('SC', 'Santa Catarina'), - ('SE', 'Sergipe'), - ('SP', 'São Paulo'), - ('TO', 'Tocantins') +REGION_CHOICES = ( + (None, 'Null'), + ('N', 'Norte'), + ('NE', 'Nordeste'), + ('CO', 'Centro-Oeste'), + ('SE', 'Sudeste'), + ('S', 'Sul') +) + +INCOME_CHOICES = ( + (None, 'Null'), + ('E', '0.00-1874.00'), + ('D', '1874.01-3748.00'), + ('C', '3748.01-9370.00'), + ('B', '9370.01-18740.00'), + ('A', '18740.01+') ) EDUCATION_CHOICES = ( - ('N', 'Null'), - ('EFC', 'Ensino Fundamental Completo'), - ('EFI', 'Ensino Fundamental Incompleto'), - ('EMC', 'Ensino Médio Completo'), - ('EMI', 'Ensino Médio Incompleto'), - ('ESC', 'Ensino Superior Completo'), - ('ESI', 'Ensino Superior Incompleto'), - ('PG', 'Pós-Graduação'), - ('ME', 'Mestrado'), - ('DO', 'Doutorado'), - ('PD', 'Pós-Doutorado') + (None, 'Null'), + ('SE', 'Sem Escolaridade'), + ('EF', 'Ensino Fundamental'), + ('EM', 'Ensino Médio'), + ('ES', 'Ensino Superior'), + ('PG', 'Pós-Graduação') +) + +RACE_CHOICES = ( + (None, 'Null'), + ('B', 'Branca'), + ('PR', 'Preta'), + ('A', 'Amarela'), + ('PA', 'Parda'), + ('I', 'Indígena') +) + +GENDER_CHOICES = ( + (None, 'Null'), + ('M', 'Masculino'), + ('F', 'Feminino'), + ('O', 'Outros') ) VOTE_CHOICES = ( @@ -57,22 +56,6 @@ ('M', 'Missing'), ) -GENDER_CHOICES = ( - ('M', 'Male'), - ('F', 'Female'), -) - -INCOME_CHOICES = ( - ('-1', 'Null'), - ('0', '0.00-1000.00'), - ('1', '1000.01-3000.00'), - ('2', '3000.01-6000.00'), - ('3', '6000.01-9000.00'), - ('4', '9000.01-15000.00'), - ('5', '15000.01-25000.00'), - ('6', '25000.00+'), -) - CONTACT_CHOICES = ( ('A', 'Dúvida'), ('B', 'Sugestão'), @@ -88,22 +71,47 @@ class SocialInformation(models.Model): related_name='social_information', on_delete=models.CASCADE ) - federal_unit = models.CharField( - max_length=150, - choices=UF_CHOICES, - default='N' + region = models.CharField( + max_length=2, + choices=REGION_CHOICES, + default=None, + null=True ) - city = models.CharField(max_length=150, blank=True) income = models.CharField( - max_length=100, + max_length=20, choices=INCOME_CHOICES, - default='-1' + default=None, + null=True ) education = models.CharField( - max_length=150, choices=EDUCATION_CHOICES, default='N' + max_length=2, + choices=EDUCATION_CHOICES, + default=None, + null=True + ) + race = models.CharField( + max_length=2, + choices=RACE_CHOICES, + default=None, + null=True + ) + gender = models.CharField( + max_length=1, + choices=GENDER_CHOICES, + default=None, + null=True ) - job = models.CharField(max_length=100, blank=True) - birth_date = models.DateField(default=datetime.date.today) + birth_date = models.DateField( + default=None, + null=True + ) + + def __str__(self): + return '{owner}'.format(owner=self.owner) + + class Meta: + verbose_name = "Social Information" + verbose_name_plural = "Social Informations" class Parliamentary(models.Model): @@ -123,6 +131,10 @@ class Parliamentary(models.Model): def __str__(self): return '{name}'.format(name=self.name) + class Meta: + verbose_name = "Parliamentary" + verbose_name_plural = "Parliamentarians" + class Proposition(models.Model): @@ -135,12 +147,17 @@ class Proposition(models.Model): processing = models.CharField(max_length=100, blank=True) situation = models.CharField(max_length=100, blank=True) url_full = models.URLField(blank=True) + last_update = models.DateTimeField(blank=True) def __str__(self): return 'Proposition {native_id}'.format( native_id=self.native_id ) + class Meta: + verbose_name = "Proposition" + verbose_name_plural = "Propositions" + class UserVote(models.Model): @@ -162,6 +179,8 @@ class UserVote(models.Model): class Meta: unique_together = ('proposition', 'user') + verbose_name = "User Vote" + verbose_name_plural = "User Votes" class ParliamentaryVote(models.Model): @@ -184,6 +203,8 @@ class ParliamentaryVote(models.Model): class Meta: unique_together = ('proposition', 'parliamentary') + verbose_name = "Parliamentary Vote" + verbose_name_plural = "Parliamentary Votes" class UserFollowing(models.Model): @@ -199,6 +220,10 @@ class UserFollowing(models.Model): related_name='followers' ) + class Meta: + verbose_name = "User Following" + verbose_name_plural = "User Following" + class Compatibility(models.Model): @@ -216,6 +241,10 @@ class Compatibility(models.Model): matching_votes = models.IntegerField(blank=True) compatibility = models.FloatField(blank=True) + class Meta: + verbose_name = "Compatibility" + verbose_name_plural = "Compatibilities" + class ExtendedUser(models.Model): @@ -226,6 +255,10 @@ class ExtendedUser(models.Model): ) should_update = models.BooleanField(default=True) + class Meta: + verbose_name = "Extended User" + verbose_name_plural = "Extended Users" + class ContactUs(models.Model): topic = models.CharField(max_length=150) @@ -236,3 +269,7 @@ class ContactUs(models.Model): default='A' ) text = models.CharField(max_length=500) + + class Meta: + verbose_name = "Contact Us" + verbose_name_plural = "Contact Us" diff --git a/api/permissions.py b/api/permissions.py index 5f093e2..30775ee 100644 --- a/api/permissions.py +++ b/api/permissions.py @@ -15,7 +15,7 @@ def has_permission(self, request, view): elif (request.user.is_anonymous and request.method == 'POST'): return True - elif 'users' in request.path: + elif 'users' in request.path and 'actual_user' not in request.path: url_id = request.path.split('/users/')[1][:-1] user_id = str(request.user.id) diff --git a/api/serializers.py b/api/serializers.py index 5defd96..d523297 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -13,11 +13,11 @@ class Meta: fields = [ 'id', 'owner', - 'federal_unit', - 'city', + 'region', 'income', 'education', - 'job', + 'race', + 'gender', 'birth_date', ] @@ -55,14 +55,18 @@ def create(self, validated_data): return voxpopuser def update(self, instance, validated_data): - updatedUser = vars(instance) - del(updatedUser['_state']) + + updated_user = vars(instance) + del updated_user['_state'] for field, value in validated_data.items(): - updatedUser[field] = value - updated = User(**updatedUser) + updated_user[field] = value + + updated = User(**updated_user) if 'password' in validated_data: updated.set_password(validated_data['password']) + updated.save() + return updated @@ -96,7 +100,8 @@ class Meta: 'abstract', 'processing', 'situation', - 'url_full' + 'url_full', + 'last_update' ] diff --git a/api/tests.py b/api/tests.py index ac79143..7684c21 100644 --- a/api/tests.py +++ b/api/tests.py @@ -28,6 +28,9 @@ def setUp(self): email='teste@teste.com', password='teste' ) + self.social_information = SocialInformation.objects.create( + owner=self.user + ) self.url = '/api/users/' # self.client.force_login(self.user) self.client.force_authenticate(self.superuser) @@ -37,6 +40,7 @@ def tearDown(self): This method will run after any test. """ self.user.delete() + self.social_information.delete() def test_create_user(self): """ @@ -63,6 +67,7 @@ def test_update_user(self): """ Ensure we can create a new user object. """ + self.client.force_authenticate(self.user) self.assertEqual(self.user.username, 'teste') data = { 'username':'teste', @@ -102,6 +107,7 @@ def test_partial_update_user(self): """ Ensure we can partially update a user object. """ + self.client.force_authenticate(self.user) self.assertEqual(self.user.email, 'teste@teste.com') data = { 'email':'silverson@teste.com', @@ -171,12 +177,12 @@ def test_invalid_create_social(self): """ data = { "owner": self.user, - "federal_unit": "AC", - "city": "Rio Branco", - "income": "2", - "education": "EFC", - "job": "Dono de Casa", - "birth_date": "2018-01-01" + "region": "", + "income": "", + "education": "", + "race": "", + "gender": "", + "birth_date": "" } response = None @@ -191,34 +197,34 @@ def test_update_social(self): """ Ensure we can update a new social information object. """ - self.assertEqual(self.social.job, '') + self.assertEqual(self.social.region, None) data = { "owner": self.user.pk, - "federal_unit": "AC", - "city": "Rio Branco", - "income": "2", - "education": "EFC", - "job": "Dono de Prédio", - "birth_date": "2018-04-07" + "region": "N", + "income": "", + "education": "", + "race": "", + "gender": "", + "birth_date": "" } response = self.client.put(self.url + str(self.social.pk) + '/', data) new_social = SocialInformation.objects.get(pk=self.social.pk) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(new_social.job, 'Dono de Prédio') + self.assertEqual(new_social.region, 'N') def test_invalid_update_social(self): """ Ensure we can't update a social object with invalid fields. """ - self.assertEqual(self.social.birth_date, datetime.date.today()) + self.assertEqual(self.social.birth_date, None) data = { "owner": self.user.pk, - "federal_unit": "AC", - "city": "Rio Branco", - "income": "2", - "education": "EFC", - "job": "Dono de Casa", + "region": "", + "income": "", + "education": "", + "race": "", + "gender": "", "birth_date": "20180-43-213" } response = self.client.put(self.url + str(self.social.pk) + '/', data) @@ -236,21 +242,21 @@ def test_partial_update_social(self): """ Ensure we can partially update a social object. """ - self.assertEqual(self.social.job, '') + self.assertEqual(self.social.region, None) data = { - 'job':'Dono de Condomínio', + 'region': 'N', } response = self.client.patch(self.url + str(self.social.pk) + '/', data) new_social = SocialInformation.objects.get(pk=self.social.pk) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(new_social.job, 'Dono de Condomínio') + self.assertEqual(new_social.region, 'N') def test_invalid_partial_update_social(self): """ Ensure we can't partially update invalid information on a valid social object. """ - self.assertEqual(self.social.job, '') + self.assertEqual(self.social.region, None) data = { 'birth_date': '20180-56-89', } diff --git a/api/views.py b/api/views.py index 1228c7e..6e702dd 100644 --- a/api/views.py +++ b/api/views.py @@ -1,10 +1,12 @@ import json from base64 import b64encode +from datetime import datetime from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist from django.db import IntegrityError from django.db.models import Count, Q +from django.utils import timezone from rest_framework import mixins, status, viewsets from rest_framework.authtoken.views import ObtainAuthToken @@ -300,6 +302,8 @@ def create(self, request): try: social_information_data = request.data['social_information'] + if social_information_data.get('id'): + del social_information_data['id'] except KeyError: social_information_data = {} @@ -427,8 +431,59 @@ def update(self, request, pk=None, **kwargs): request, pk, **kwargs) + + try: + social_information_data = request.data['social_information'] + if social_information_data.get('id'): + del social_information_data['id'] + if social_information_data.get('owner'): + del social_information_data['owner'] + + SocialInformation.objects.filter(owner=request.user).update( + **social_information_data + ) + except KeyError: + pass + + social_information = request.user.social_information + social_information_serializer = \ + SocialInformationSerializer(social_information) + response.data['social_information'] = \ + social_information_serializer.data + return response + @list_route(methods=['get']) + def actual_user(self, request): + """ + Returns the actual user. + """ + + if request.user.is_authenticated: + user = dict() + user['id'] = request.user.id + user['username'] = request.user.username + user['first_name'] = request.user.first_name + user['last_name'] = request.user.last_name + + try: + social_information = request.user.social_information + except SocialInformation.DoesNotExist: + social_information = SocialInformation.objects.create( + owner=request.user + ) + social_information.save() + + try: + extended_user = request.user.extended_user + except ExtendedUser.DoesNotExist: + extended_user = ExtendedUser.objects.create(user=request.user) + extended_user.save() + else: + user = User.objects.none() + + return Response(user) + class LoaderViewSet(ViewSet): """ @@ -490,7 +545,7 @@ def get_propositions(self, request): if request.query_params.get('key') == \ LoaderViewSet.__get_credentials(): - propositions = Proposition.objects.all().order_by('-year') + propositions = Proposition.objects.all().order_by('-last_update') propositions_list = [] for proposition in propositions: @@ -518,6 +573,12 @@ def create_proposition(self, request): if request.query_params.get('key') == \ LoaderViewSet.__get_credentials(): proposition_dict = request.data.dict() + + proposition_dict['last_update'] = datetime.strptime( + proposition_dict['last_update'] + '-0300', + '%Y-%m-%dT%H:%M%z' + ) + Proposition.objects.create(**proposition_dict) response = Response({"status": "OK"}, status=status.HTTP_200_OK) @@ -624,7 +685,7 @@ class PropositionViewset(mixins.RetrieveModelMixin, serializer_class = PropositionSerializer def get_queryset(self): - queryset = Proposition.objects.all().order_by('-year') + queryset = Proposition.objects.all().order_by('-last_update') return propositions_filter(self, queryset) def list(self, request): @@ -713,7 +774,7 @@ def non_voted_by_user(self, request): all_propositions = Proposition.objects.filter( id__in=proposition_voted_ids - ).order_by('-year') + ).order_by('-last_update') response = Response( {'status': 'No Content'}, @@ -755,6 +816,12 @@ def non_voted_by_user(self, request): round(parliamentarians_approval, 2) response.data['population_approval'] = \ round(population_approval, 2) + response.data['days_ago'] = ( + timezone.now() - datetime.strptime( + response.data['last_update'] + '-0300', + '%Y-%m-%dT%H:%M:%SZ%z' + ) + ).days break @@ -782,7 +849,7 @@ def voted_by_parliamentary(self, request): queryset = Proposition.objects.filter( id__in=proposition_voted_ids - ).order_by('-year') + ).order_by('-last_update') # serializer = PropositionSerializer(queryset, many=True) # return Response(serializer.data) @@ -819,6 +886,10 @@ def voted_by_parliamentary(self, request): round(parliamentarians_approval, 2) proposition['population_approval'] = \ round(population_approval, 2) + proposition['days_ago'] = (timezone.now() - datetime.strptime( + proposition['last_update'] + '-0300', + '%Y-%m-%dT%H:%M:%SZ%z' + )).days return self.get_paginated_response(serializer.data) @@ -875,6 +946,12 @@ def list(self, request): round(parliamentarians_approval, 2) vote['proposition']['population_approval'] = \ round(population_approval, 2) + vote['proposition']['days_ago'] = ( + timezone.now() - datetime.strptime( + vote['proposition']['last_update'] + '-0300', + '%Y-%m-%dT%H:%M:%SZ%z' + ) + ).days return response diff --git a/provision/hml/templates/settings.j2 b/provision/hml/templates/settings.j2 index 7541de4..24c19fc 100644 --- a/provision/hml/templates/settings.j2 +++ b/provision/hml/templates/settings.j2 @@ -44,6 +44,9 @@ INSTALLED_APPS = [ 'rest_framework_swagger', 'corsheaders', 'django_celery_beat', + 'oauth2_provider', + 'social_django', + 'rest_framework_social_oauth2', 'api', ] @@ -73,6 +76,8 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'social_django.context_processors.backends', + 'social_django.context_processors.login_redirect', ], }, }, @@ -85,9 +90,33 @@ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.TokenAuthentication', + 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', + 'rest_framework_social_oauth2.authentication.SocialAuthentication', ) } +AUTHENTICATION_BACKENDS = ( + # Facebook OAuth2 + 'social_core.backends.facebook.FacebookAppOAuth2', + 'social_core.backends.facebook.FacebookOAuth2', + + # django-rest-framework-social-oauth2 + 'rest_framework_social_oauth2.backends.DjangoOAuth2', + + # Django + 'django.contrib.auth.backends.ModelBackend', +) + +# Facebook configuration +SOCIAL_AUTH_FACEBOOK_KEY = '650913118581699' +SOCIAL_AUTH_FACEBOOK_SECRET = '5aeb28e721ea337a89f096d99746e1c3' + +# Define SOCIAL_AUTH_FACEBOOK_SCOPE to get extra permissions from facebook. Email is not sent by default, to get it, you must request the email permission: +SOCIAL_AUTH_FACEBOOK_SCOPE = ['email'] +SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = { + 'fields': 'id, name, email' +} + WSGI_APPLICATION = 'voxpopapi.wsgi.application' diff --git a/provision/prod/templates/settings.j2 b/provision/prod/templates/settings.j2 index 3925f45..8871853 100644 --- a/provision/prod/templates/settings.j2 +++ b/provision/prod/templates/settings.j2 @@ -44,6 +44,9 @@ INSTALLED_APPS = [ 'rest_framework_swagger', 'corsheaders', 'django_celery_beat', + 'oauth2_provider', + 'social_django', + 'rest_framework_social_oauth2', 'api', ] @@ -73,6 +76,8 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'social_django.context_processors.backends', + 'social_django.context_processors.login_redirect', ], }, }, @@ -85,9 +90,33 @@ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.TokenAuthentication', + 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', + 'rest_framework_social_oauth2.authentication.SocialAuthentication', ) } +AUTHENTICATION_BACKENDS = ( + # Facebook OAuth2 + 'social_core.backends.facebook.FacebookAppOAuth2', + 'social_core.backends.facebook.FacebookOAuth2', + + # django-rest-framework-social-oauth2 + 'rest_framework_social_oauth2.backends.DjangoOAuth2', + + # Django + 'django.contrib.auth.backends.ModelBackend', +) + +# Facebook configuration +SOCIAL_AUTH_FACEBOOK_KEY = '650913118581699' +SOCIAL_AUTH_FACEBOOK_SECRET = '5aeb28e721ea337a89f096d99746e1c3' + +# Define SOCIAL_AUTH_FACEBOOK_SCOPE to get extra permissions from facebook. Email is not sent by default, to get it, you must request the email permission: +SOCIAL_AUTH_FACEBOOK_SCOPE = ['email'] +SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = { + 'fields': 'id, name, email' +} + WSGI_APPLICATION = 'voxpopapi.wsgi.application' diff --git a/requirements.txt b/requirements.txt index d8d7ffa..916605e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ django-cors-headers django-celery-beat redis coverage +django-rest-framework-social-oauth2 diff --git a/voxpopapi/settings.py b/voxpopapi/settings.py index e88094c..b393cf8 100644 --- a/voxpopapi/settings.py +++ b/voxpopapi/settings.py @@ -44,6 +44,9 @@ 'rest_framework_swagger', 'corsheaders', 'django_celery_beat', + 'oauth2_provider', + 'social_django', + 'rest_framework_social_oauth2', 'api', ] @@ -73,6 +76,8 @@ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'social_django.context_processors.backends', + 'social_django.context_processors.login_redirect', ], }, }, @@ -85,9 +90,33 @@ 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.TokenAuthentication', + 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', + 'rest_framework_social_oauth2.authentication.SocialAuthentication', ) } +AUTHENTICATION_BACKENDS = ( + # Facebook OAuth2 + 'social_core.backends.facebook.FacebookAppOAuth2', + 'social_core.backends.facebook.FacebookOAuth2', + + # django-rest-framework-social-oauth2 + 'rest_framework_social_oauth2.backends.DjangoOAuth2', + + # Django + 'django.contrib.auth.backends.ModelBackend', +) + +# Facebook configuration +SOCIAL_AUTH_FACEBOOK_KEY = '650913118581699' +SOCIAL_AUTH_FACEBOOK_SECRET = '5aeb28e721ea337a89f096d99746e1c3' + +# Define SOCIAL_AUTH_FACEBOOK_SCOPE to get extra permissions from facebook. Email is not sent by default, to get it, you must request the email permission: +SOCIAL_AUTH_FACEBOOK_SCOPE = ['email'] +SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = { + 'fields': 'id, name, email' +} + WSGI_APPLICATION = 'voxpopapi.wsgi.application' diff --git a/voxpopapi/urls.py b/voxpopapi/urls.py index fbe28d1..10220f7 100644 --- a/voxpopapi/urls.py +++ b/voxpopapi/urls.py @@ -30,4 +30,5 @@ url(r'^api/', include('rest_framework.urls', namespace='rest_framework')), url(r'^api/token_auth/', CustomObtainToken.as_view()), url(r'^$', schema_view), + url(r'^api/oauth/', include('rest_framework_social_oauth2.urls')), ]