Skip to content

Commit

Permalink
Merge pull request #226 from nossas/feature/city-and-state-model-fields
Browse files Browse the repository at this point in the history
Feat: Adiciona Modelagem de Estados e Municipios com Select2
  • Loading branch information
miguelzinh3 authored Jul 15, 2024
2 parents d56d11a + 4bc89ac commit fac5e44
Show file tree
Hide file tree
Showing 8 changed files with 10,845 additions and 18 deletions.
10,671 changes: 10,671 additions & 0 deletions app/votepeloclima/candidature/csv/places.csv

Large diffs are not rendered by default.

45 changes: 43 additions & 2 deletions app/votepeloclima/candidature/forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from django import forms
from captcha.widgets import ReCaptchaV2Checkbox

from django_select2.forms import Select2Widget
from django.utils.functional import lazy
from django.urls import reverse_lazy

from .fields import ValidateOnceReCaptchaField
from .locations_utils import get_ufs, get_choices


class CaptchaForm(forms.Form):
Expand All @@ -20,13 +25,49 @@ class InitialForm(forms.Form):
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")
state = forms.ChoiceField(
label="Estado",
choices=lazy(get_ufs, list)(),
widget=Select2Widget(
attrs={
"data-address-fields": "state",
"data-address-url": reverse_lazy("address"),
}
)
)
city = forms.ChoiceField(
choices=[],
label="Cidade",
widget=Select2Widget(
attrs={
"data-address-fields": "city",
"data-address-url": reverse_lazy("address"),
}
)
)
is_collective_mandate = forms.BooleanField(
label="É um mandato coletivo?", required=False
)
political_party = forms.CharField(label="Partido político")

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'state' in self.data:
self.fields['city'].choices = get_choices(self.data.get('state'))
elif self.initial.get('state'):
self.fields['city'].choices = get_choices(self.initial.get('state'))
class Media:
css = {
"all": [
"https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css",
]
}
js = [
"https://code.jquery.com/jquery-3.5.1.min.js",
"https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.full.min.js",
"https://cdn.jsdelivr.net/npm/[email protected]/dist/js/i18n/pt-BR.js",
"js/address-fields.js",
]

class ProfileForm(forms.Form):
video = forms.URLField(label="Vídeo", required=False)
Expand Down
34 changes: 34 additions & 0 deletions app/votepeloclima/candidature/locations_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from django.conf import settings
from pathlib import Path
import csv
from typing import List

def read_csv_file(file_path: Path) -> List[dict]:
with open(file_path) as f:
reader = csv.DictReader(f)
reader.fieldnames = [field.strip() for field in reader.fieldnames]
return [row for row in reader]

def get_states(column_label="Nome_UF"):
csv_filename = Path(settings.BASE_DIR) / "votepeloclima/candidature/csv/places.csv"
rows = read_csv_file(csv_filename)
states = set()
for row in rows:
uf = row["UF"].strip()
state_name = row[column_label].strip()
states.add((uf, state_name))
return sorted(list(states), key=lambda x: x[1])

def get_ufs():
return get_states(column_label="Nome_UF")

def get_choices(uf):
csv_filename = Path(settings.BASE_DIR) / "votepeloclima/candidature/csv/places.csv"
rows = read_csv_file(csv_filename)
choices = []
for row in rows:
if row["UF"].strip() == uf:
city_code = row["Código Município Completo"].strip()
city_name = row["Nome_Município"].strip()
choices.append((city_code, city_name))
return sorted(choices, key=lambda x: x[1])
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2 on 2024-07-09 19:43

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


class Migration(migrations.Migration):

dependencies = [
('candidature', '0004_alter_candidatureflow_properties'),
]

operations = [
migrations.AlterField(
model_name='candidatureflow',
name='candidature',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='candidature.candidature'),
),
migrations.AlterField(
model_name='candidatureflow',
name='status',
field=models.CharField(choices=[('draft', 'Editando'), ('submitted', 'Enviado'), ('invalid', 'Inválido'), ('is_valid', 'Válido'), ('draft_requested', 'Edição Requisitada')], default='draft', max_length=50),
),
]
45 changes: 45 additions & 0 deletions app/votepeloclima/candidature/static/js/address-fields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
(function ($) {
"use strict";

$(function () {
const $stateField = $('[data-address-fields="state"]');
const $cityField = $('[data-address-fields="city"]');

if ($stateField.length) {
$stateField.prepend('<option value=""></option>').val("");
$stateField.select2({
allowClear: true,
dropdownAutoWidth: true,
width: "auto",
placeholder: "Selecione seu estado"
});

$cityField.prepend('<option value=""></option>').val("");
$cityField.select2({
allowClear: true,
dropdownAutoWidth: true,
width: "auto",
placeholder: "Selecione sua cidade"
});

var uf;
$stateField.on("change", (evt) => {
uf = evt.target.value;
const url = $stateField.data("address-url");

$cityField.empty();
$cityField.append('<option value="">Selecione sua cidade</option>');

if (uf) {
$.get(url + "?state=" + uf, (data) => {
$.each(data, (index, value) => {
$cityField.append(
'<option value="' + value.code + '">' + value.name + '</option>'
);
});
});
}
});
}
});
}(jQuery));
30 changes: 15 additions & 15 deletions app/votepeloclima/candidature/templates/candidature/done.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{% for step in checkout_steps %}
{% if step.name == 'checkout' %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ wizard.form.media }}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form.as_p }}
{% endfor %}
{% else %}
{{ wizard.form.as_p }}
{% endif %}
<button type="submit">Concluir</button>
</form>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ wizard.form.media }}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form.as_p }}
{% endfor %}
{% else %}
{{ wizard.form.as_p }}
{% endif %}
<button type="submit">Concluir</button>
</form>
{% else %}
<div>
<a href="{{ step.edit_url }}">Editar</a>
{{ step.form.as_p }}
</div>
{% endif %}
{% endfor %}
{% endfor %}
11 changes: 11 additions & 0 deletions app/votepeloclima/candidature/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from django.shortcuts import redirect
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.http import JsonResponse
from django.views import View
from django.contrib.auth.models import User
from django.views.generic import TemplateView
from django.http import HttpResponseForbidden
Expand All @@ -10,6 +13,7 @@
from contrib.oauth.utils import send_confirmation_email
from .models import CandidatureFlow, CandidatureFlowStatus, Candidature
from .forms import register_form_list, InitialForm, FlagForm, AppointmentForm
from .locations_utils import get_choices


class RegisterView(NamedUrlSessionWizardView):
Expand Down Expand Up @@ -181,3 +185,10 @@ def get_context_data(self, **kwargs):
})

return context


class AddressView(View):
def get(self, request, *args, **kwargs):
state = request.GET.get('state')
cities = get_choices(state)
return JsonResponse([{'code': code, 'name': name} for code, name in cities], safe=False)
3 changes: 2 additions & 1 deletion app/votepeloclima/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import path, include, re_path

from .candidature.views import RegisterView, EditRegisterView, DashboardView
from .candidature.views import RegisterView, EditRegisterView, DashboardView, AddressView

register_view = RegisterView.as_view(url_name="register_step", done_step_name="concluir")
register_edit_view = EditRegisterView.as_view(url_name="register_edit_step", done_step_name="concluir")
Expand All @@ -42,6 +42,7 @@
path("oauth/", DashboardView.as_view()),
path("admin/", admin.site.urls),
path("select2/", include("django_select2.urls")),
path('address/', AddressView.as_view(), name='address'),
path("", include("cms.urls")),
]

Expand Down

0 comments on commit fac5e44

Please sign in to comment.