Skip to content

Commit

Permalink
Merge pull request #223 from nossas/feature/workflow-register-candida…
Browse files Browse the repository at this point in the history
…ture

feat(vote): add flow with checkout step and register form on cookie
  • Loading branch information
igr-santos authored Jul 9, 2024
2 parents 3978758 + 27a3c5d commit 0a4a6e5
Show file tree
Hide file tree
Showing 19 changed files with 560 additions and 7 deletions.
5 changes: 5 additions & 0 deletions app/contrib/oauth/templates/oauth/change_password.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Enviar</button>
</form>
3 changes: 2 additions & 1 deletion app/contrib/oauth/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from django.urls import path, include

from django.contrib.auth.views import LogoutView
from .views import OAuthIndexView, OAuthLoginView
from .views import OAuthIndexView, OAuthLoginView, OAuthChangePasswordView


urlpatterns = [
Expand All @@ -29,5 +29,6 @@
# path("select2/", include("django_select2.urls")),
path("logout/", LogoutView.as_view(), name="logout"),
path("login/", OAuthLoginView.as_view(), name="login"),
path("change-password/<uidb64>/<token>/", OAuthChangePasswordView.as_view(), name="change-password"),
path("", OAuthIndexView.as_view(), name="index"),
]
30 changes: 30 additions & 0 deletions app/contrib/oauth/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.contrib.auth.tokens import default_token_generator
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode


def get_uuid_and_token(user):
return urlsafe_base64_encode(
force_bytes(user.pk)
), default_token_generator.make_token(user)



def send_confirmation_email(user, request, email_template_name):
uid, token = get_uuid_and_token(user)
current_site = request.current_site

activation_url = f'http{"s" if request.is_secure() else ""}://{current_site.domain}{reverse("oauth:change-password", kwargs={"uidb64": uid, "token": token})}'

subject = "Confirme seu e-mail"
message = render_to_string(email_template_name, {
"user": user,
"activation_url": activation_url
})
to_email = user.email

email = EmailMessage(subject, message, to=[to_email])
email.send()
34 changes: 32 additions & 2 deletions app/contrib/oauth/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django.views.generic import TemplateView
from django.http import HttpResponseRedirect
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import LoginView
from django.contrib.auth.views import LoginView, PasswordResetConfirmView, INTERNAL_RESET_SESSION_TOKEN
from django.contrib.auth import login as auth_login
from django.urls import reverse_lazy, reverse


Expand All @@ -17,4 +19,32 @@ class OAuthLoginView(LoginView):
template_name = "oauth/login.html"

def get_success_url(self):
return reverse("oauth:index")
return reverse("oauth:index")


class OAuthChangePasswordView(PasswordResetConfirmView):
template_name = "oauth/change_password.html"
post_reset_login = True
post_reset_login_backend = "contrib.oauth.backends.OAuthBackend"

def form_valid(self, form):
user = form.save(commit=False)
# Active user after reset password
user.is_active = True
user.save()

try:
del self.request.session[INTERNAL_RESET_SESSION_TOKEN]
except KeyError as err:
print(err)

if self.post_reset_login:
auth_login(self.request, user, self.post_reset_login_backend)
return HttpResponseRedirect(self.get_success_url())

def get_success_url(self):
return reverse("oauth:index")
# form_class = PasswordChangeForm

# def form_valid(self, form):
# return super().form_valid()
Empty file.
15 changes: 15 additions & 0 deletions app/votepeloclima/candidature/fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import sys
from captcha.fields import ReCaptchaField


class ValidateOnceReCaptchaField(ReCaptchaField):
def clean(self, values):
# find the 'revalidating' value in stack
frame = sys._getframe()
max_depth = 25
while frame and max_depth != 0:
if "revalid" in frame.f_locals:
return values[0]
max_depth -= 1
frame = frame.f_back
return super(ValidateOnceReCaptchaField, self).clean(values)
68 changes: 68 additions & 0 deletions app/votepeloclima/candidature/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from django import forms
from captcha.widgets import ReCaptchaV2Checkbox

from .fields import ValidateOnceReCaptchaField


class CaptchaForm(forms.Form):
captcha = ValidateOnceReCaptchaField(widget=ReCaptchaV2Checkbox())


class InitialForm(forms.Form):
legal_name = forms.CharField(label="Nome")
ballot_name = forms.CharField(label="Nome na urna")
birth_date = forms.DateField(label="Data de nascimento")
email = forms.EmailField(label="E-mail")
cpf_cnpj = forms.CharField(label="CPF/CNPJ")
tse_id = forms.CharField(label="Identificação TSE (?)", required=False)


class ApplicationForm(forms.Form):
number_id = forms.IntegerField(label="Número de identificação", min_value=1)
intended_position = forms.CharField(label="Cargo pretendido")
state = forms.CharField(label="Estado")
city = forms.CharField(label="Cidade")
is_collective_mandate = forms.BooleanField(
label="É um mandato coletivo?", required=False
)
political_party = forms.CharField(label="Partido político")


class ProfileForm(forms.Form):
video = forms.URLField(label="Vídeo", required=False)
photo = forms.URLField(label="Foto", required=False)
gender = forms.CharField(label="Gênero")
color = forms.CharField(label="Raça")
sexuality = forms.CharField(label="Sexualidade", required=False)


class TrackForm(forms.Form):
education = forms.CharField(label="Escolaridade", required=False)
employment = forms.CharField(label="Ocupação", required=False)
short_description = forms.CharField(label="Minibio", widget=forms.Textarea())


class FlagForm(forms.Form):
is_renewable_energy = forms.BooleanField(label="Energia Renovável", required=False)
is_transport_and_mobility = forms.BooleanField(label="Transporte e Mobilidade", required=False)


class AppointmentForm(forms.Form):
appointment_1 = forms.BooleanField(label="Compromisso 1", required=False)
appointment_2 = forms.BooleanField(label="Compromisso 2", required=False)


class CheckoutForm(forms.Form):
is_valid = forms.BooleanField()


register_form_list = [
("captcha", CaptchaForm),
("informacoes-iniciais", InitialForm),
("informacoes-de-candidatura", ApplicationForm),
("complemente-seu-perfil", ProfileForm),
("sobre-sua-trajetoria", TrackForm),
("bandeiras-da-sua-candidatura", FlagForm),
("compromissos", AppointmentForm),
("checkout", CheckoutForm)
]
56 changes: 56 additions & 0 deletions app/votepeloclima/candidature/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 4.2 on 2024-07-05 19:16

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='Candidature',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('legal_name', models.CharField(max_length=150)),
('ballot_name', models.CharField(max_length=100)),
('birth_date', models.DateField()),
('email', models.EmailField(max_length=254)),
('cpf_cnpj', models.CharField(max_length=30)),
('tse_id', models.CharField(blank=True, max_length=30, null=True)),
('number_id', models.PositiveIntegerField()),
('intended_position', models.CharField(max_length=50)),
('state', models.CharField(max_length=2)),
('city', models.CharField(max_length=60)),
('is_collective_mandate', models.BooleanField(blank=True, default=False)),
('political_party', models.CharField(max_length=60)),
('video', models.FileField(blank=True, null=True, upload_to='cadidatures/videos/')),
('photo', models.FileField(blank=True, null=True, upload_to='cadidatures/photos/')),
('gender', models.CharField(max_length=30)),
('color', models.CharField(max_length=30)),
('sexuality', models.CharField(blank=True, max_length=30, null=True)),
('education', models.CharField(blank=True, max_length=50, null=True)),
('employment', models.CharField(blank=True, max_length=50, null=True)),
('short_description', models.TextField()),
('flags', models.JSONField(blank=True)),
('appointments', models.JSONField(blank=True)),
],
),
migrations.CreateModel(
name='CandidatureState',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('properties', models.JSONField(blank=True)),
('status', models.CharField(max_length=50)),
('validations', models.JSONField(blank=True)),
('candidature', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='candidature.candidature')),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2 on 2024-07-08 14:18

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('candidature', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='CandidatureFlow',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('properties', models.JSONField(blank=True)),
('status', models.CharField(choices=[('draft', 'Rascunhando'), ('submitted', 'Enviado'), ('invalid', 'Inválido'), ('is_valid', 'Validado'), ('draft_requested', 'Edição Requisitada')], default='draft', max_length=50)),
('validations', models.JSONField(blank=True)),
('candidature', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='candidature.candidature')),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.DeleteModel(
name='CandidatureState',
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 4.2 on 2024-07-08 14:35

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('candidature', '0002_candidatureflow_delete_candidaturestate'),
]

operations = [
migrations.AlterField(
model_name='candidatureflow',
name='properties',
field=models.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name='candidatureflow',
name='user',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='candidatureflow',
name='validations',
field=models.JSONField(blank=True, null=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2 on 2024-07-08 15:41

import django.core.serializers.json
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('candidature', '0003_alter_candidatureflow_properties_and_more'),
]

operations = [
migrations.AlterField(
model_name='candidatureflow',
name='properties',
field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
),
]
Empty file.
Loading

0 comments on commit 0a4a6e5

Please sign in to comment.