From 068ceba7a54990d9054ab52cee1d49d31acf801d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20=C3=81lvarez?= Date: Fri, 3 Aug 2018 19:26:27 -0300 Subject: [PATCH] Merepresenta complete profile candidate --- backend_candidate/forms.py | 5 +- backend_candidate/models.py | 7 + backend_candidate/tests/candidacy_tests.py | 9 + backend_candidate/views.py | 9 +- backend_citizen/image_getter_from_social.py | 8 +- backend_citizen/tasks.py | 10 +- merepresenta/candidatos/forms.py | 22 ++- merepresenta/candidatos/strategy.py | 5 + merepresenta/candidatos/urls.py | 12 +- merepresenta/candidatos/views.py | 59 +++++-- merepresenta/forms.py | 63 ++++++- merepresenta/models.py | 17 +- .../backend_candidate/complete_profile.html | 20 +-- .../templates/backend_candidate/menu.html | 2 +- .../templates/candidatos/cpf_and_ddn2.html | 36 ++++ .../candidatos/login_with_facebook.html | 11 ++ .../merepresenta/candidate_detail.html | 2 +- .../templates/merepresenta/index.html | 2 +- .../login_and_first_page_view_tests.py | 136 ++++++++++++--- .../candidatos/update_profile_view_tests.py | 162 ++++++++++++++++++ merepresenta/tests/model_tests.py | 19 +- merepresenta/tests/volunteers/views_tests.py | 21 +++ merepresenta/voluntarios/views.py | 8 +- votainteligente/votainteligente_settings.py | 1 + 24 files changed, 570 insertions(+), 76 deletions(-) create mode 100644 merepresenta/templates/candidatos/cpf_and_ddn2.html create mode 100644 merepresenta/templates/candidatos/login_with_facebook.html create mode 100644 merepresenta/tests/candidatos/update_profile_view_tests.py diff --git a/backend_candidate/forms.py b/backend_candidate/forms.py index ad3b60a9..6f445908 100644 --- a/backend_candidate/forms.py +++ b/backend_candidate/forms.py @@ -133,7 +133,7 @@ def _import(name): for comp in components[1:]: try: mod = getattr(mod, comp) - except AttributeError: + except AttributeError as e: raise ImportError return mod @@ -211,8 +211,7 @@ def get_candidate_profile_form_class(): if settings.THEME: try: PARENT_FORM_CLASS = _import(settings.THEME + ".forms.PersonalDataForm") - - except ImportError: + except ImportError as e: from votai_general_theme.forms import PersonalDataForm PARENT_FORM_CLASS = PersonalDataForm else: diff --git a/backend_candidate/models.py b/backend_candidate/models.py index 84d8f753..9921899f 100644 --- a/backend_candidate/models.py +++ b/backend_candidate/models.py @@ -23,6 +23,13 @@ class Candidacy(models.Model): null=True) + def get_complete_profile_url(self): + url = reverse('backend_candidate:complete_profile', + kwargs={'slug': self.candidate.election.slug, + 'candidate_slug': self.candidate.slug}) + return url + + def is_candidate(user): if not user.is_authenticated(): return False diff --git a/backend_candidate/tests/candidacy_tests.py b/backend_candidate/tests/candidacy_tests.py index 29b3dfe1..12058ec0 100644 --- a/backend_candidate/tests/candidacy_tests.py +++ b/backend_candidate/tests/candidacy_tests.py @@ -267,6 +267,15 @@ def test_post_complete_12_naranja(self): self.assertTrue(TakenPosition.objects.filter(person=self.candidate)) self.assertRedirects(response, url) + def test_candidacy_get_complete_profile_url(self): + candidacy = Candidacy.objects.create(user=self.feli, + candidate=self.candidate + ) + url = reverse('backend_candidate:complete_profile', + kwargs={'slug': self.candidate.election.slug, + 'candidate_slug': self.candidate.slug}) + self.assertEquals(candidacy.get_complete_profile_url(), url) + class CandidacyContacts(CandidacyTestCaseBase): def setUp(self): diff --git a/backend_candidate/views.py b/backend_candidate/views.py index 37a8a735..29c8cf57 100644 --- a/backend_candidate/views.py +++ b/backend_candidate/views.py @@ -139,19 +139,20 @@ def get_redirect_url(self, *args, **kwargs): return profile_url -class ProfileView(FormView): +class ProfileView(LoginRequiredMixin, FormView): template_name = 'backend_candidate/complete_profile.html' + candidate_model = Candidate + election_model = Election def get_form_class(self): return get_candidate_profile_form_class() - @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): if not is_candidate(request.user): raise Http404 self.user = request.user - self.election = get_object_or_404(Election, slug=self.kwargs['slug']) - self.candidate = get_object_or_404(Candidate, + self.election = get_object_or_404(self.election_model, slug=self.kwargs['slug']) + self.candidate = get_object_or_404(self.candidate_model, slug=self.kwargs['candidate_slug']) if not Candidacy.objects.filter(user=self.request.user, candidate=self.candidate).exists(): raise Http404 diff --git a/backend_citizen/image_getter_from_social.py b/backend_citizen/image_getter_from_social.py index 83af8b24..01c0f97c 100644 --- a/backend_citizen/image_getter_from_social.py +++ b/backend_citizen/image_getter_from_social.py @@ -1,4 +1,5 @@ -from backend_citizen.tasks import save_image_to_user +from backend_citizen.tasks import _save_image_to_user, save_image_to_user +from django.conf import settings def get_image_from_social(backend, strategy, details, response, user=None, *args, **kwargs): @@ -11,6 +12,9 @@ def get_image_from_social(backend, strategy, details, response, ext = url.split('.')[-1] if url: try: - save_image_to_user.delay(url, user) + if settings.EAGER_USER_IMAGE: + _save_image_to_user(url, user) + else: + save_image_to_user.delay(url, user) except Exception as e: pass diff --git a/backend_citizen/tasks.py b/backend_citizen/tasks.py index 9d93b4b9..88a9e3f9 100644 --- a/backend_citizen/tasks.py +++ b/backend_citizen/tasks.py @@ -11,8 +11,12 @@ def image_getter(url): return File(img_temp) -@app.task -def save_image_to_user(url, user): +def _save_image_to_user(url, user): f = image_getter(url) + name = "profile_%s.jpg" % user.username user.profile.image = f - user.profile.image.save(user.username, f) \ No newline at end of file + user.profile.image.save(name, f) + +@app.task +def save_image_to_user(url, user): + _save_image_to_user(url, user) \ No newline at end of file diff --git a/merepresenta/candidatos/forms.py b/merepresenta/candidatos/forms.py index 1bd30548..3707d098 100644 --- a/merepresenta/candidatos/forms.py +++ b/merepresenta/candidatos/forms.py @@ -3,11 +3,11 @@ from backend_candidate.models import Candidacy from merepresenta.models import Candidate - -class CPFAndDdnForm(forms.Form): +class CPFAndDdnFormBase(forms.Form): cpf = forms.CharField(required=True) nascimento = forms.DateField(required=True, input_formats=['%d/%m/%Y','%d/%m/%y', '%d-%m-%Y', '%d-%m-%y',]) +class CPFAndDdnForm(CPFAndDdnFormBase): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user') super(CPFAndDdnForm, self).__init__(*args, **kwargs) @@ -24,4 +24,20 @@ def clean(self): return self.cleaned_data def save(self): - return Candidacy.objects.create(user=self.user, candidate=self.candidate) \ No newline at end of file + return Candidacy.objects.create(user=self.user, candidate=self.candidate) + + +class CPFAndDdnForm2(CPFAndDdnFormBase): + def clean(self): + cpf = self.cleaned_data['cpf'] + ddn = self.cleaned_data.get('nascimento', None) + if ddn is None: + raise forms.ValidationError(u'Não encontramos o candidato') + try: + self.candidate = Candidate.objects.get(cpf=cpf, data_de_nascimento=ddn) + except Candidate.DoesNotExist: + raise forms.ValidationError(u'Não encontramos o candidato') + return self.cleaned_data + + def get_candidate(self): + return self.candidate \ No newline at end of file diff --git a/merepresenta/candidatos/strategy.py b/merepresenta/candidatos/strategy.py index 65753913..5e9af430 100644 --- a/merepresenta/candidatos/strategy.py +++ b/merepresenta/candidatos/strategy.py @@ -1,6 +1,8 @@ from social_django.strategy import DjangoStrategy from django.shortcuts import resolve_url from django.contrib.auth import authenticate +from backend_candidate.models import Candidacy +from merepresenta.models import Candidate class CandidateStrategy(DjangoStrategy): @@ -12,6 +14,9 @@ def get_setting(self, name): def create_user(self, *args, **kwargs): user = super(CandidateStrategy, self).create_user(*args, **kwargs) + slug = self.session_get('facebook_slug') + c = Candidate.objects.get(slug=slug) + Candidacy.objects.create(user=user, candidate=c) return user def authenticate(self, backend, *args, **kwargs): diff --git a/merepresenta/candidatos/urls.py b/merepresenta/candidatos/urls.py index 69f0a404..b5525dd8 100644 --- a/merepresenta/candidatos/urls.py +++ b/merepresenta/candidatos/urls.py @@ -2,24 +2,32 @@ from django.conf.urls import url, include from .views import (LoginView, CPFAndDDNSelectView, + CPFAndDDNSelectView2, + CompleteProfileView, complete, auth ) urlpatterns = [ - url(r'^login/?$', + url(r'^login_after/?$', LoginView.as_view(), name='candidate_login'), url(r'^cpf_e_ddn/?$', CPFAndDDNSelectView.as_view(), name='cpf_and_date'), + url(r'^login/?$', + CPFAndDDNSelectView2.as_view(), + name='cpf_and_date2'), + url(r'^profile/(?P[-\w]+)/(?P[-\w]+)/?$', + CompleteProfileView.as_view(), + name='merepresenta_complete_profile'), url(r'^complete/(?P[^/]+)$', complete, name='candidate_social_complete' ), - url(r'^login/(?P[^/]+)/?$', auth, + url(r'^login/(?P[^/]+)/(?P[^/]+)?$', auth, name='candidato_social_begin'), ] diff --git a/merepresenta/candidatos/views.py b/merepresenta/candidatos/views.py index 8d781ecb..f3b8dc69 100644 --- a/merepresenta/candidatos/views.py +++ b/merepresenta/candidatos/views.py @@ -3,7 +3,8 @@ from django.core.urlresolvers import reverse_lazy from django.http import HttpResponseNotFound, HttpResponseRedirect from backend_candidate.models import Candidacy -from .forms import CPFAndDdnForm +from backend_candidate.views import ProfileView +from .forms import CPFAndDdnForm, CPFAndDdnForm2 from social_core.actions import do_complete, do_auth from social_django.views import _do_login from django.views.decorators.cache import never_cache @@ -11,8 +12,13 @@ from social_django.utils import psa, STORAGE, get_strategy from django.views.generic.base import TemplateView from backend_candidate.models import is_candidate - - +from django.template.response import TemplateResponse +from django.shortcuts import get_object_or_404 +from django.utils.decorators import method_decorator +from django.contrib.auth.decorators import login_required +from merepresenta.models import Candidate +from django.core.urlresolvers import reverse +from merepresenta.forms import PersonalDataForm class LoginView(TemplateView): template_name="candidatos/login.html" @@ -37,7 +43,7 @@ def dispatch(self, *args, **kwargs): c = Candidacy.objects.get(user=self.request.user) election = c.candidate.election candidate = c.candidate - url = reverse_lazy('backend_candidate:complete_profile', kwargs={'slug': election.slug, 'candidate_slug': candidate.slug}) + url = reverse_lazy('merepresenta_complete_profile', kwargs={'slug': election.slug, 'candidate_slug': candidate.slug}) return HttpResponseRedirect(url) except Candidacy.DoesNotExist: pass @@ -56,12 +62,29 @@ def form_valid(self, form): def get_success_url(self): c = Candidacy.objects.get(user=self.request.user) - election = c.candidate.election - candidate = c.candidate - url = reverse_lazy('backend_candidate:complete_profile', kwargs={'slug': election.slug, 'candidate_slug': candidate.slug}) - return url + return c.get_complete_profile_url() + + +class CPFAndDDNSelectView2(FormView): + form_class = CPFAndDdnForm2 + template_name = 'candidatos/cpf_and_ddn2.html' + def dispatch(self, *args, **kwargs): + if self.request.user.is_staff: + return HttpResponseNotFound() + if is_candidate(self.request.user): + user = self.request.user + candidacy = user.candidacies.first() + url = reverse_lazy('merepresenta_complete_profile', kwargs={'slug': candidacy.candidate.election.slug, + 'candidate_slug': candidacy.candidate.slug}) + return HttpResponseRedirect(url) + return super(CPFAndDDNSelectView2, self).dispatch(*args, **kwargs) + + def form_valid(self, form): + self.candidate = form.get_candidate() + context = {'candidate': self.candidate} + return TemplateResponse(self.request, template='candidatos/login_with_facebook.html', context=context) def load_strategy(request=None): return get_strategy('merepresenta.candidatos.strategy.CandidateStrategy', STORAGE, request) @@ -76,5 +99,21 @@ def complete(request, backend, *args, **kwargs): @never_cache @psa('candidate_social_complete') -def auth(request, backend): - return do_auth(request.backend) \ No newline at end of file +def auth(request, backend, slug): + candidate = get_object_or_404(Candidate, slug=slug) + request.backend.strategy.session_set('facebook_slug', slug) + return do_auth(request.backend) + + +class CompleteProfileView(ProfileView): + candidate_model = Candidate + + def get_form_class(self): + return PersonalDataForm + + def get_success_url(self): + url = reverse('merepresenta_complete_profile', + kwargs={'slug': self.election.slug, + 'candidate_slug': self.candidate.slug} + ) + return url \ No newline at end of file diff --git a/merepresenta/forms.py b/merepresenta/forms.py index 709d08e4..df315ee2 100644 --- a/merepresenta/forms.py +++ b/merepresenta/forms.py @@ -4,13 +4,72 @@ from popular_proposal.models import PopularProposal from django.core.cache import cache from django.conf import settings -from elections.models import Area, QuestionCategory +from elections.models import Area, QuestionCategory, PersonalData from django.contrib.sites.models import Site from medianaranja2.proposals_getter import ByOnlySiteProposalGetter -from merepresenta.models import MeRepresentaPopularProposal +from merepresenta.models import MeRepresentaPopularProposal, Candidate from django.utils.safestring import mark_safe from medianaranja2.adapters import Adapter as OriginalAdapter +GENDERS = [ + ('feminino', u"Feminino"), + ('masculino', u"Masculino"), + ('outro', u"Outro gênero"), +] +RACES = [ + ('BRANCA', u"Branca"), + ('PRETA', u"Preta"), + ('PARDA', u"Parda"), + ('AMARELA', u"Amarela"), + ('INDÍGENA', u"Indígena"), +] + +class PersonalDataForm(forms.Form): + email = forms.EmailField(label=u"Para manter contato, quais desses e-mails você mais usa?") + gender = forms.ChoiceField(choices=GENDERS, label=u'Com qual desses gêneros você se identifica?') + lgbt = forms.BooleanField(label=u'Você se declara LGBT?') + race = forms.ChoiceField(label=u'Qual é a sua cor ou raça?', choices=RACES) + bio = forms.CharField(label=u"Escreva um pouco sobre você", widget=forms.TextInput) + candidatura_coletiva = forms.BooleanField(label=u'Você faz parte de uma Candidatura Coletiva?') + renovacao_politica = forms.CharField(label=u"Você faz parte de algum grupo de Renovação Política? Qual?") + + + def __init__(self, *args, **kwargs): + self.candidate = kwargs.pop('candidate') + initial = {} + if 'initial' in kwargs: + kwargs['initial'].update(initial) + else: + kwargs['initial'] = initial + personal_datas_as_dict = {} + for personal_data in self.candidate.personal_datas.all(): + personal_datas_as_dict[personal_data.label] = personal_data.value + + for field in self.__class__.base_fields: + candidate_has_field = hasattr(self.candidate, field) + if candidate_has_field: + value = getattr(self.candidate, field, None) + kwargs['initial'][field] = value + else: + kwargs['initial'][field] = personal_datas_as_dict.get(field, None) + super(PersonalDataForm, self).__init__(*args, **kwargs) + + + def save(self): + + for f_name in self.cleaned_data: + if hasattr(self.candidate, f_name): + setattr(self.candidate, f_name, self.cleaned_data[f_name]) + else: + personal_data, created = PersonalData.objects.get_or_create(candidate=self.candidate, + label=f_name) + personal_data.value = self.cleaned_data[f_name] + if personal_data.value is None: + personal_data.value = '' + personal_data.save() + self.candidate.save() + return self.candidate + class MeRepresentaProposalModelMultipleChoiceField(forms.ModelMultipleChoiceField): template_name = 'merepresenta/checkbox_select.html' diff --git a/merepresenta/models.py b/merepresenta/models.py index 416e6e90..06017dc2 100644 --- a/merepresenta/models.py +++ b/merepresenta/models.py @@ -28,7 +28,7 @@ def save(self, *args, **kwargs): NON_MALE_KEY = "F" -NON_WHITE_KEY = {"possible_values": ["PARDA", "PARDA"]} +NON_WHITE_KEY = {"possible_values": ["PARDA", "PRETA"]} class ForVolunteersManager(models.Manager): @@ -76,6 +76,21 @@ class Candidate(OriginalCandidate): for_volunteers = LimitCandidatesForVolunteers() + def get_image(self): + print "oli" + if self.candidacy_set.exists(): + user = self.candidacy_set.first().user + return user.profile.image + return self.image + + # @property + # def image(self): + # """I'm the 'x' property.""" + # if self.candidacies.exists(): + # user = self.candidacies.first().user + # return user.profile.image + # return self.image + class VolunteerInCandidate(models.Model): volunteer = models.ForeignKey(User) candidate = models.ForeignKey(Candidate) diff --git a/merepresenta/templates/backend_candidate/complete_profile.html b/merepresenta/templates/backend_candidate/complete_profile.html index af5c1da5..70578fa7 100644 --- a/merepresenta/templates/backend_candidate/complete_profile.html +++ b/merepresenta/templates/backend_candidate/complete_profile.html @@ -14,30 +14,18 @@

Perfil de {{candidacy.candidate.name}}

{% endwith %} -
{% csrf_token %} + {% csrf_token %} - {% if candidate.image %} - {% thumbnail candidate.image "300x300" crop="center" as im %} - {{candidate.name}} + {% if candidate.candidacy_set.first.user.profile.image %} + {% thumbnail candidate.candidacy_set.first.user.profile.image "300x300" crop="center" as im %} + {{candidate.name}} {% endthumbnail %} {% else %} {{candidate.name}} {% endif %} - {% bootstrap_field form.image %} {% for field in form %} - {% ifnotequal field.name 'image' %} - {% ifequal field.name 'program_link' %} - {% ifequal candidate.election.position 'Presidenta o Presidente' %} - {% bootstrap_field field %} - {% else %} - {% bootstrap_label "Link a tus ideas y planteamientos" %} - {% bootstrap_field field show_label=False %} - {% endifequal %} - {% else %} {% bootstrap_field field %} - {% endifequal %} - {% endifnotequal %} {% endfor %} diff --git a/merepresenta/templates/backend_candidate/menu.html b/merepresenta/templates/backend_candidate/menu.html index 02847bde..1489f935 100644 --- a/merepresenta/templates/backend_candidate/menu.html +++ b/merepresenta/templates/backend_candidate/menu.html @@ -4,7 +4,7 @@ {% load votainteligente_extras %} {% for candidacy in user.candidacies.all %} - {% url 'backend_candidate:complete_profile' slug=candidacy.candidate.election.slug candidate_slug=candidacy.candidate.slug as complete_profile %} + {% url 'merepresenta_complete_profile' slug=candidacy.candidate.election.slug candidate_slug=candidacy.candidate.slug as complete_profile %}