From 9907b24457c20bdf1185fd81a11a9285fe2bdf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Thu, 6 Jan 2022 20:51:42 -0300 Subject: [PATCH] Migra app Contatos --- sigi/apps/contatos/admin.py | 55 ++++--- sigi/apps/contatos/apps.py | 6 + sigi/apps/contatos/filters.py | 46 ------ sigi/apps/contatos/migrations/0001_initial.py | 13 +- .../migrations/0002_auto_20151104_0810.py | 6 +- ...ons_alter_microrregiao_options_and_more.py | 133 +++++++++++++++ sigi/apps/contatos/models.py | 153 +++++++++--------- sigi/apps/contatos/tests.py | 3 + sigi/apps/utils/__init__.py | 2 +- sigi/apps/utils/base_admin.py | 1 - sigi/apps/utils/filters.py | 97 +++++++++-- sigi/settings/base.py | 1 + 12 files changed, 338 insertions(+), 178 deletions(-) create mode 100644 sigi/apps/contatos/apps.py delete mode 100644 sigi/apps/contatos/filters.py create mode 100644 sigi/apps/contatos/migrations/0005_alter_mesorregiao_options_alter_microrregiao_options_and_more.py create mode 100644 sigi/apps/contatos/tests.py diff --git a/sigi/apps/contatos/admin.py b/sigi/apps/contatos/admin.py index 22b1d07..c20dc24 100644 --- a/sigi/apps/contatos/admin.py +++ b/sigi/apps/contatos/admin.py @@ -1,12 +1,12 @@ -# -*- coding: utf-8 -*- from django.contrib import admin from django.utils.translation import gettext as _ -from sigi.apps.contatos.filters import PopulationFilter -from sigi.apps.contatos.models import (UnidadeFederativa, Mesorregiao, Microrregiao, - Municipio, Telefone, Contato) +from sigi.apps.utils.filters import RangeFilter +from sigi.apps.contatos.models import (UnidadeFederativa, Mesorregiao, + Microrregiao, Municipio, Telefone, + Contato) from sigi.apps.utils import queryset_ascii -from sigi.apps.utils.base_admin import BaseModelAdmin + class MesorregiaoInline(admin.TabularInline): model = Mesorregiao @@ -14,35 +14,43 @@ class MesorregiaoInline(admin.TabularInline): class MicrorregiaoInline(admin.TabularInline): model = Microrregiao -class UnidadeFederativaAdmin(BaseModelAdmin): +@admin.register(UnidadeFederativa) +class UnidadeFederativaAdmin(admin.ModelAdmin): actions = None list_display = ('codigo_ibge', 'nome', 'sigla', 'regiao', 'populacao') list_display_links = ('codigo_ibge', 'nome') - list_filter = ('regiao', 'populacao', PopulationFilter,) + list_filter = ('regiao', ('populacao', RangeFilter),) search_fields = ('search_text', 'codigo_ibge', 'sigla', 'regiao') get_queryset = queryset_ascii inlines = (MesorregiaoInline, ) -class MesorregiaoAdmin(BaseModelAdmin): +@admin.register(Mesorregiao) +class MesorregiaoAdmin(admin.ModelAdmin): actions = None list_display = ('codigo_ibge', 'uf', 'nome') list_display_links = ('codigo_ibge', 'nome') list_filter = ('uf',) - search_fields = ('uf__search_text', 'search_text', 'codigo_ibge', 'uf__sigla') + search_fields = ('uf__search_text', 'search_text', 'codigo_ibge', + 'uf__sigla') get_queryset = queryset_ascii inlines = (MicrorregiaoInline,) -class MunicipioAdmin(BaseModelAdmin): +@admin.register(Municipio) +class MunicipioAdmin(admin.ModelAdmin): actions = None - list_display = ('codigo_ibge', 'codigo_tse', 'nome', 'uf', 'is_capital', 'populacao', 'is_polo', 'idh', 'pib_ano', - 'pib_total', 'pib_percapita') + list_display = ('codigo_ibge', 'codigo_tse', 'nome', 'uf', 'is_capital', + 'populacao', 'is_polo', 'idh', 'pib_ano', 'pib_total', + 'pib_percapita') list_display_links = ('codigo_ibge', 'codigo_tse', 'nome') - list_filter = ('is_capital', 'is_polo', 'idh', 'populacao', 'uf', ) + list_filter = ('is_capital', 'is_polo', ('idh', RangeFilter), + ('populacao', RangeFilter), 'uf__regiao', 'uf', ) get_queryset = queryset_ascii fieldsets = ( (None, { - 'fields': ('codigo_ibge', 'codigo_tse', 'nome', 'data_criacao', 'uf', 'microrregiao', - 'is_capital', 'populacao', 'is_polo', 'idh', 'pib_ano', 'pib_total', 'pib_percapita') + 'fields': ('codigo_ibge', 'codigo_tse', 'nome', 'data_criacao', + 'uf', 'microrregiao', 'is_capital', 'populacao', + 'is_polo', 'idh', 'pib_ano', 'pib_total', + 'pib_percapita') }), (_('Posição geográfica'), { 'fields': ('latitude', 'longitude'), @@ -50,23 +58,18 @@ class MunicipioAdmin(BaseModelAdmin): ) search_fields = ('search_text', 'codigo_ibge', 'codigo_tse', 'uf__sigla') - -class TelefoneAdmin(BaseModelAdmin): +@admin.register(Telefone) +class TelefoneAdmin(admin.ModelAdmin): list_display = ('numero', 'tipo', 'nota') list_display_links = ('numero',) list_filter = ('tipo',) radio_fields = {'tipo': admin.VERTICAL} search_fields = ('numero', 'tipo', 'nota') - -class ContatoAdmin(BaseModelAdmin): +@admin.register(Contato) +class ContatoAdmin(admin.ModelAdmin): list_display = ('nome', 'nota', 'email', 'municipio') list_display_links = ('nome',) list_filter = ('nome',) - search_fields = ('nome', 'nota', 'email', 'municipio__nome', 'municipio__uf__nome') - -admin.site.register(UnidadeFederativa, UnidadeFederativaAdmin) -admin.site.register(Mesorregiao, MesorregiaoAdmin) -admin.site.register(Municipio, MunicipioAdmin) -admin.site.register(Telefone, TelefoneAdmin) -admin.site.register(Contato, ContatoAdmin) + search_fields = ('nome', 'nota', 'email', 'municipio__nome', + 'municipio__uf__nome') diff --git a/sigi/apps/contatos/apps.py b/sigi/apps/contatos/apps.py new file mode 100644 index 0000000..2dd9391 --- /dev/null +++ b/sigi/apps/contatos/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + +class ContatosConfig(AppConfig): + name = 'sigi.apps.contatos' + verbose_name = _('contatos') diff --git a/sigi/apps/contatos/filters.py b/sigi/apps/contatos/filters.py deleted file mode 100644 index a83fb1c..0000000 --- a/sigi/apps/contatos/filters.py +++ /dev/null @@ -1,46 +0,0 @@ -# coding: utf-8 -from django.contrib import admin -from django.utils.translation import gettext as _ - - -class PopulationFilter(admin.SimpleListFilter): - # Human-readable title which will be displayed in the - # right admin sidebar just above the filter options. - title = _('População') - - # Parameter for the filter that will be used in the URL query. - parameter_name = 'faixa' - - def lookups(self, request, model_admin): - """ - Returns a list of tuples. The first element in each - tuple is the coded value for the option that will - appear in the URL query. The second element is the - human-readable name for the option that will appear - in the right sidebar. - """ - return ( - ('1', _('< 100 Mil')), - ('2', _('100 Mil a 1 Milhão')), - ('3', _('1 Milhão a 100 Milhões')), - ('4', _('> 100 Milhões')), - ) - - def queryset(self, request, queryset): - """ - Returns the filtered queryset based on the value - provided in the query string and retrievable via - `self.value()`. - """ - # Compare the requested value (either '1', '2', '3' or '4') - # to decide how to filter the queryset. - if self.value() == '1': - return queryset.filter(populacao__lt=100000) - elif self.value() == '2': - return queryset.filter(populacao__gte=100000, populacao__lt=1000000) - elif self.value() == '3': - return queryset.filter(populacao__gte=1000000, populacao__lt=10000000) - elif self.value() == '4': - return queryset.filter(populacao__gt=100000000) - else: - return queryset diff --git a/sigi/apps/contatos/migrations/0001_initial.py b/sigi/apps/contatos/migrations/0001_initial.py index 9541e20..95f1a0c 100644 --- a/sigi/apps/contatos/migrations/0001_initial.py +++ b/sigi/apps/contatos/migrations/0001_initial.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations @@ -21,7 +20,7 @@ class Migration(migrations.Migration): ('nota', models.CharField(max_length=70, blank=True)), ('email', models.EmailField(max_length=75, verbose_name='e-mail', blank=True)), ('object_id', models.PositiveIntegerField()), - ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)), ], options={ 'ordering': ('nome',), @@ -42,7 +41,7 @@ class Migration(migrations.Migration): ('bairro', models.CharField(max_length=100, blank=True)), ('cep', models.CharField(help_text='Formato: XXXXX-XXX.', max_length=9, null=True, verbose_name='CEP', blank=True)), ('object_id', models.PositiveIntegerField()), - ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)), ], options={ 'ordering': ('logradouro', 'numero'), @@ -87,7 +86,7 @@ class Migration(migrations.Migration): ('nota', models.CharField(max_length=70, null=True, blank=True)), ('ult_alteracao', models.DateTimeField(auto_now=True, verbose_name='\xdaltima altera\xe7\xe3o', null=True)), ('object_id', models.PositiveIntegerField()), - ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ('content_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)), ], options={ 'ordering': ('numero',), @@ -118,19 +117,19 @@ class Migration(migrations.Migration): migrations.AddField( model_name='municipio', name='uf', - field=models.ForeignKey(verbose_name='UF', to='contatos.UnidadeFederativa'), + field=models.ForeignKey(verbose_name='UF', to='contatos.UnidadeFederativa', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='endereco', name='municipio', - field=models.ForeignKey(verbose_name='munic\xedpio', blank=True, to='contatos.Municipio', null=True), + field=models.ForeignKey(verbose_name='munic\xedpio', blank=True, to='contatos.Municipio', null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='contato', name='municipio', - field=models.ForeignKey(verbose_name='munic\xedpio', blank=True, to='contatos.Municipio', null=True), + field=models.ForeignKey(verbose_name='munic\xedpio', blank=True, to='contatos.Municipio', null=True, on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/sigi/apps/contatos/migrations/0002_auto_20151104_0810.py b/sigi/apps/contatos/migrations/0002_auto_20151104_0810.py index c95d8c2..b35632a 100644 --- a/sigi/apps/contatos/migrations/0002_auto_20151104_0810.py +++ b/sigi/apps/contatos/migrations/0002_auto_20151104_0810.py @@ -18,7 +18,7 @@ class Migration(migrations.Migration): ('codigo_ibge', models.PositiveIntegerField(help_text='C\xf3digo da mesorregi\xe3o segundo o IBGE', unique=True, serialize=False, verbose_name='C\xf3digo IBGE', primary_key=True)), ('nome', models.CharField(max_length=100, verbose_name='Nome mesorregi\xe3o')), ('search_text', sigi.apps.utils.SearchField(field_names=[b'nome'], editable=False)), - ('uf', models.ForeignKey(verbose_name='UF', to='contatos.UnidadeFederativa')), + ('uf', models.ForeignKey(verbose_name='UF', to='contatos.UnidadeFederativa', on_delete=models.CASCADE)), ], options={ 'ordering': ('uf', 'nome'), @@ -33,7 +33,7 @@ class Migration(migrations.Migration): ('codigo_ibge', models.PositiveIntegerField(help_text='C\xf3digo da microrregi\xe3o segundo o IBGE', unique=True, serialize=False, verbose_name='C\xf3digo IBGE', primary_key=True)), ('nome', models.CharField(max_length=100, verbose_name='Nome microrregi\xe3o')), ('search_text', sigi.apps.utils.SearchField(field_names=[b'nome'], editable=False)), - ('mesorregiao', models.ForeignKey(to='contatos.Mesorregiao')), + ('mesorregiao', models.ForeignKey(to='contatos.Mesorregiao', on_delete=models.CASCADE)), ], options={ 'ordering': ('mesorregiao', 'nome'), @@ -53,7 +53,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='municipio', name='microrregiao', - field=models.ForeignKey(verbose_name='Microrregi\xe3o', blank=True, to='contatos.Microrregiao', null=True), + field=models.ForeignKey(verbose_name='Microrregi\xe3o', blank=True, to='contatos.Microrregiao', null=True, on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/sigi/apps/contatos/migrations/0005_alter_mesorregiao_options_alter_microrregiao_options_and_more.py b/sigi/apps/contatos/migrations/0005_alter_mesorregiao_options_alter_microrregiao_options_and_more.py new file mode 100644 index 0000000..941d434 --- /dev/null +++ b/sigi/apps/contatos/migrations/0005_alter_mesorregiao_options_alter_microrregiao_options_and_more.py @@ -0,0 +1,133 @@ +# Generated by Django 4.0.1 on 2022-01-06 23:50 + +from django.db import migrations, models +import django.db.models.deletion +import sigi.apps.utils + + +class Migration(migrations.Migration): + + dependencies = [ + ('contatos', '0004_auto_20210611_0946'), + ] + + operations = [ + migrations.AlterModelOptions( + name='mesorregiao', + options={'ordering': ('uf', 'nome'), 'verbose_name': ('mesorregião',), 'verbose_name_plural': 'mesorregiões'}, + ), + migrations.AlterModelOptions( + name='microrregiao', + options={'ordering': ('nome',), 'verbose_name': 'microrregião', 'verbose_name_plural': 'microrregiões'}, + ), + migrations.AlterField( + model_name='contato', + name='email', + field=models.EmailField(blank=True, max_length=254, verbose_name='e-mail'), + ), + migrations.AlterField( + model_name='contato', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='endereco', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='endereco', + name='logradouro', + field=models.CharField(max_length=100, verbose_name='logradouro'), + ), + migrations.AlterField( + model_name='endereco', + name='tipo', + field=models.CharField(choices=[('aeroporto', 'Aeroporto'), ('alameda', 'Alameda'), ('area', 'Área'), ('avenida', 'Avenida'), ('campo', 'Campo'), ('chacara', 'Chácara'), ('colonia', 'Colônia'), ('condominio', 'Condomínio'), ('conjunto', 'Conjunto'), ('distrito', 'Distrito'), ('esplanada', 'Esplanada'), ('estacao', 'Estação'), ('estrada', 'Estrada'), ('favela', 'Favela'), ('fazenda', 'Fazenda'), ('feira', 'Feira'), ('jardim', 'Jardim'), ('ladeira', 'Ladeira'), ('lago', 'Lago'), ('lagoa', 'Lagoa'), ('largo', 'Largo'), ('loteamento', 'Loteamento'), ('morro', 'Morro'), ('nucleo', 'Núcleo'), ('parque', 'Parque'), ('passarela', 'Passarela'), ('patio', 'Pátio'), ('praca', 'Praça'), ('quadra', 'Quadra'), ('recanto', 'Recanto'), ('residencial', 'Residencial'), ('rodovia', 'Rodovia'), ('rua', 'Rua'), ('setor', 'Setor'), ('sitio', 'Sítio'), ('travessa', 'Travessa'), ('trecho', 'Trecho'), ('trevo', 'Trevo'), ('vale', 'Vale'), ('vereda', 'Vereda'), ('via', 'Via'), ('viaduto', 'Viaduto'), ('viela', 'Viela'), ('vila', 'Vila'), ('outro', 'Outro')], max_length=15, verbose_name='tipo'), + ), + migrations.AlterField( + model_name='mesorregiao', + name='codigo_ibge', + field=models.PositiveIntegerField(help_text='Código da mesorregião segundo o IBGE', primary_key=True, serialize=False, unique=True, verbose_name='código IBGE'), + ), + migrations.AlterField( + model_name='mesorregiao', + name='nome', + field=models.CharField(max_length=100, verbose_name='nome mesorregião'), + ), + migrations.AlterField( + model_name='mesorregiao', + name='search_text', + field=sigi.apps.utils.SearchField(editable=False, field_names=['nome']), + ), + migrations.AlterField( + model_name='microrregiao', + name='codigo_ibge', + field=models.PositiveIntegerField(help_text='Código da microrregião segundo o IBGE', primary_key=True, serialize=False, unique=True, verbose_name='código IBGE'), + ), + migrations.AlterField( + model_name='microrregiao', + name='mesorregiao', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contatos.mesorregiao', verbose_name='mesorregião'), + ), + migrations.AlterField( + model_name='microrregiao', + name='nome', + field=models.CharField(max_length=100, verbose_name='nome microrregião'), + ), + migrations.AlterField( + model_name='microrregiao', + name='search_text', + field=sigi.apps.utils.SearchField(editable=False, field_names=['nome']), + ), + migrations.AlterField( + model_name='municipio', + name='latitude', + field=models.DecimalField(blank=True, decimal_places=8, help_text='Exemplo: -20,464.', max_digits=10, null=True, verbose_name='latitude'), + ), + migrations.AlterField( + model_name='municipio', + name='longitude', + field=models.DecimalField(blank=True, decimal_places=8, help_text='Exemplo: -45,426.', max_digits=11, null=True, verbose_name='longitude'), + ), + migrations.AlterField( + model_name='municipio', + name='microrregiao', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='contatos.microrregiao', verbose_name='microrregião'), + ), + migrations.AlterField( + model_name='telefone', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='telefone', + name='tipo', + field=models.CharField(choices=[('F', 'Fixo'), ('M', 'Móvel'), ('X', 'Fax'), ('I', 'Indefinido')], default='I', max_length=1, verbose_name='tipo'), + ), + migrations.AlterField( + model_name='telefone', + name='ult_alteracao', + field=models.DateTimeField(auto_now=True, null=True, verbose_name='última alteração'), + ), + migrations.AlterField( + model_name='unidadefederativa', + name='nome', + field=models.CharField(max_length=25, verbose_name='nome UF'), + ), + migrations.AlterField( + model_name='unidadefederativa', + name='regiao', + field=models.CharField(choices=[('CO', 'Centro-Oeste'), ('NE', 'Nordeste'), ('NO', 'Norte'), ('SD', 'Sudeste'), ('SL', 'Sul')], max_length=2, verbose_name='região'), + ), + migrations.AlterField( + model_name='unidadefederativa', + name='search_text', + field=sigi.apps.utils.SearchField(editable=False, field_names=['nome']), + ), + migrations.AlterField( + model_name='unidadefederativa', + name='sigla', + field=models.CharField(help_text='Exemplo: MG.', max_length=2, unique=True, verbose_name='sigla'), + ), + ] diff --git a/sigi/apps/contatos/models.py b/sigi/apps/contatos/models.py index 87282b2..6a00d5e 100644 --- a/sigi/apps/contatos/models.py +++ b/sigi/apps/contatos/models.py @@ -1,18 +1,13 @@ -# -*- coding: utf-8 -*- from django.contrib.contenttypes.fields import (GenericForeignKey, GenericRelation) from django.contrib.contenttypes.models import ContentType from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.utils.translation import gettext as _ - from sigi.apps.utils import SearchField class UnidadeFederativa(models.Model): - - """ Modelo que representa um estado brasileiro - """ REGIAO_CHOICES = ( ('CO', _('Centro-Oeste')), ('NE', _('Nordeste')), @@ -21,18 +16,19 @@ class UnidadeFederativa(models.Model): ('SL', _('Sul')), ) codigo_ibge = models.PositiveIntegerField( - 'código IBGE', + _('código IBGE'), primary_key=True, unique=True, help_text=_('Código do estado segundo IBGE.') ) - nome = models.CharField(_('Nome UF'), max_length=25) + nome = models.CharField(_('nome UF'), max_length=25) # Campo de busca em caixa baixa sem acento search_text = SearchField(field_names=['nome']) sigla = models.CharField( + _('sigla'), max_length=2, unique=True, - help_text=_("Exemplo") + ": MG.", + help_text=_("Exemplo: MG."), ) regiao = models.CharField(_('região'), max_length=2, choices=REGIAO_CHOICES) populacao = models.PositiveIntegerField(_('população')) @@ -42,12 +38,12 @@ class UnidadeFederativa(models.Model): verbose_name = _('Unidade Federativa') verbose_name_plural = _('Unidades Federativas') - def __unicode__(self): + def __str__(self): return self.nome class Mesorregiao(models.Model): codigo_ibge = models.PositiveIntegerField( - _('Código IBGE'), + _('código IBGE'), primary_key=True, unique=True, help_text=_('Código da mesorregião segundo o IBGE') @@ -57,59 +53,57 @@ class Mesorregiao(models.Model): on_delete=models.CASCADE, verbose_name=_('UF') ) - nome = models.CharField(_("Nome mesorregião"), max_length=100) + nome = models.CharField(_("nome mesorregião"), max_length=100) # Campo de busca em caixa baixa sem acento search_text = SearchField(field_names=['nome']) class Meta: ordering = ('uf', 'nome',) - verbose_name, verbose_name_plural = _('Mesorregião'), _('Mesorregiões') + verbose_name = _('mesorregião'), + verbose_name_plural = _('mesorregiões') - def __unicode__(self): + def __str__(self): return self.nome class Microrregiao(models.Model): codigo_ibge = models.PositiveIntegerField( - _('Código IBGE'), + _('código IBGE'), primary_key=True, unique=True, help_text=_('Código da microrregião segundo o IBGE') ) mesorregiao = models.ForeignKey( Mesorregiao, - on_delete=models.CASCADE + on_delete=models.CASCADE, + verbose_name=_('mesorregião'), ) - nome = models.CharField(_("Nome microrregião"), max_length=100) + nome = models.CharField(_("nome microrregião"), max_length=100) # Campo de busca em caixa baixa sem acento search_text = SearchField(field_names=['nome']) class Meta: ordering = ('nome',) - verbose_name, verbose_name_plural = _('Microrregião'), _('Microrregiões') + verbose_name = _('microrregião') + verbose_name_plural = _('microrregiões') - def __unicode__(self): - return "%s (%s)" % (self.nome, self.mesorregiao.nome) + def __str__(self): + return f"{self.nome} ({self.mesorregiao.nome})" class Municipio(models.Model): - - """ Modelo para representar as cidades brasileiras - """ codigo_ibge = models.PositiveIntegerField( _('código IBGE'), primary_key=True, unique=True, help_text=_('Código do município segundo IBGE.') ) - microrregiao = models.ForeignKey( Microrregiao, on_delete=models.PROTECT, - verbose_name=_('Microrregião'), + verbose_name=_('microrregião'), blank=True, null=True ) - - # codio designado pelo Tribunal Superior Eleitoral + # codigo designado pelo Tribunal Superior Eleitoral codigo_tse = models.PositiveIntegerField( _('código TSE'), unique=True, @@ -126,51 +120,65 @@ class Municipio(models.Model): # verdadeiro se o município é capital do estado is_capital = models.BooleanField(_('capital'), default=False) populacao = models.PositiveIntegerField(_('população')) - populacao.list_filter_range = [10000, 100000, 1000000] is_polo = models.BooleanField(_('pólo'), default=False) - data_criacao = models.DateField(_('data de criação do município'), null=True, blank=True) - + data_criacao = models.DateField( + _('data de criação do município'), + null=True, + blank=True + ) # posição geográfica do município latitude = models.DecimalField( + _('latitude'), max_digits=10, decimal_places=8, null=True, blank=True, - help_text=_('Exemplo') + ': -20,464.' + help_text=_('Exemplo: -20,464.') ) longitude = models.DecimalField( + _('longitude'), max_digits=11, decimal_places=8, null=True, blank=True, - help_text=_('Exemplo') + ': -45,426.' + help_text=_('Exemplo: -45,426.') + ) + idh = models.DecimalField( + _('IDH'), + help_text=_('Índice de desenvolvimento Humano'), + max_digits=4, + decimal_places=3, + validators=[MinValueValidator(0), MaxValueValidator(1)] + ) + pib_total = models.DecimalField( + _('PIB total'), + max_digits=18, + decimal_places=3, + blank=True, + null=True + ) + pib_percapita = models.DecimalField( + _('PIB per capita'), + max_digits=18, + decimal_places=3, + blank=True, + null=True + ) + pib_ano = models.IntegerField( + _('Ano de apuração do PIB'), + blank=True, + null=True ) - - idh = models.DecimalField(_('IDH'), help_text=_('Índice de desenvolvimento Humano'), max_digits=4, decimal_places=3, - validators=[MinValueValidator(0), MaxValueValidator(1)]) - idh.list_filter_range = [0.500, 0.800] - - pib_total = models.DecimalField(_('PIB total'), max_digits=18, decimal_places=3, blank=True, null=True) - pib_percapita = models.DecimalField(_('PIB per capita'), max_digits=18, decimal_places=3, blank=True, null=True) - pib_ano = models.IntegerField(_('Ano de apuração do PIB'), blank=True, null=True) class Meta: ordering = ('nome', 'codigo_ibge') verbose_name = _('município') verbose_name_plural = _('municípios') - def __unicode__(self): - return "%s - %s" % (self.nome, self.uf) - - def get_google_maps_url(self): - return "http://maps.google.com.br/maps/mm?ie=UTF8&hl=pt-BR&t=h&ll=%s,%s&spn=1.61886,1.812744&z=9&source=embed" % \ - (self.latitude, self.longitude) - + def __str__(self): + return f"{self.nome} - {self.uf}" class Telefone(models.Model): - - """ Modelo genérico para agrupar telefones dos modulos do sistema - """ TELEFONE_CHOICES = ( ('F', _('Fixo')), ('M', _('Móvel')), @@ -179,16 +187,23 @@ class Telefone(models.Model): ) numero = models.CharField( _('número'), - max_length=64, # TODO: diminuir tamanho de campo após migração de dados - help_text=_('Exemplo') + ': (31)8851-9898.', + max_length=64, + help_text=_('Exemplo: (31)8851-9898.'), ) tipo = models.CharField( + _('tipo'), max_length=1, choices=TELEFONE_CHOICES, default='I' ) nota = models.CharField(max_length=70, null=True, blank=True) - ult_alteracao = models.DateTimeField(_('Última alteração'), null=True, blank=True, editable=False, auto_now=True) + ult_alteracao = models.DateTimeField( + _('última alteração'), + null=True, + blank=True, + editable=False, + auto_now=True + ) # guarda o tipo do objeto (classe) vinculado a esse registro content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) @@ -203,22 +218,14 @@ class Telefone(models.Model): ordering = ('numero',) unique_together = ('numero', 'tipo') - def __unicode__(self): - return unicode(self.numero) - + def __str__(self): + return self.numero class Contato(models.Model): - - """ Modelo generico para registrar contatos vinculados aos - modulos do sistema - """ nome = models.CharField(_('nome completo'), max_length=120) - nome.alphabetic_filter = True nota = models.CharField(max_length=70, blank=True) - email = models.EmailField(_('e-mail'), blank=True) telefones = GenericRelation(Telefone) - municipio = models.ForeignKey( Municipio, on_delete=models.SET_NULL, @@ -226,7 +233,6 @@ class Contato(models.Model): blank=True, null=True, ) - # guarda o tipo do objeto (classe) vinculado a esse registro content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # identificador do registro na classe vinculado a esse registro @@ -241,10 +247,9 @@ class Contato(models.Model): verbose_name = _('contato Interlegis') verbose_name_plural = _('contatos Interlegis') - def __unicode__(self): + def __str__(self): return self.nome - class Endereco(models.Model): TIPO_CHOICES = ( ('aeroporto', _('Aeroporto')), @@ -294,26 +299,22 @@ class Endereco(models.Model): ('outro', _('Outro')), ) - # tipo do endereço obtido no site dos correios - tipo = models.CharField(max_length=15, choices=TIPO_CHOICES) + tipo = models.CharField(_('tipo'), max_length=15, choices=TIPO_CHOICES) logradouro = models.CharField( + _('logradouro'), max_length=100, ) - logradouro.alphabetic_filter = True numero = models.CharField(max_length=15, blank=True) complemento = models.CharField(max_length=15, blank=True) - # campo de texto livre referencia = models.CharField(max_length=100, blank=True) bairro = models.CharField(max_length=100, blank=True) - cep = models.CharField( _('CEP'), max_length=9, blank=True, null=True, - help_text=_("Formato") + ": XXXXX-XXX." + help_text=_("Formato: XXXXX-XXX.") ) - municipio = models.ForeignKey( Municipio, on_delete=models.SET_NULL, @@ -321,8 +322,6 @@ class Endereco(models.Model): blank=True, null=True, ) - municipio.uf_filter = True - # guarda o tipo do objeto (classe) vinculado a esse registro content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # identificador do registro na classe vinculado a esse registro @@ -337,6 +336,6 @@ class Endereco(models.Model): verbose_name = _('endereço') verbose_name_plural = _('endereços') - def __unicode__(self): - return self.tipo + ' ' + self.logradouro + ', ' + self.numero \ - + ' ' + self.complemento + ' - ' + self.bairro + def __str__(self): + return (f"{self.tipo} {self.logradouro}, {self.numero}" + f"{self.complemento} - {self.bairro}") diff --git a/sigi/apps/contatos/tests.py b/sigi/apps/contatos/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/sigi/apps/contatos/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/sigi/apps/utils/__init__.py b/sigi/apps/utils/__init__.py index 987ce86..beb28bd 100644 --- a/sigi/apps/utils/__init__.py +++ b/sigi/apps/utils/__init__.py @@ -39,4 +39,4 @@ def queryset_ascii(self, request): if 'q' in request.GET: request.GET._mutable = True request.GET['q'] = to_ascii(request.GET['q']) - return admin.ModelAdmin.queryset(self, request) + return admin.ModelAdmin.get_queryset(self, request) diff --git a/sigi/apps/utils/base_admin.py b/sigi/apps/utils/base_admin.py index b76370a..394a9f8 100644 --- a/sigi/apps/utils/base_admin.py +++ b/sigi/apps/utils/base_admin.py @@ -29,6 +29,5 @@ class BaseChangeList(ChangeList): class BaseModelAdmin(admin.ModelAdmin): - def get_changelist(self, request, **kwargs): return BaseChangeList diff --git a/sigi/apps/utils/filters.py b/sigi/apps/utils/filters.py index 0551776..6a9361d 100644 --- a/sigi/apps/utils/filters.py +++ b/sigi/apps/utils/filters.py @@ -1,31 +1,94 @@ # coding: utf-8 import string from django.contrib import admin +from django.contrib.admin.options import IncorrectLookupParameters +from django.utils.translation import gettext as _ +from django.core.exceptions import ValidationError class AlphabeticFilter(admin.SimpleListFilter): - # Human-readable title which will be displayed in the - # right admin sidebar just above the filter options. title = '' - - # Parameter for the filter that will be used in the URL query. parameter_name = '' def lookups(self, request, model_admin): - """ - Returns a list of tuples. The first element in each - tuple is the coded value for the option that will - appear in the URL query. The second element is the - human-readable name for the option that will appear - in the right sidebar. - """ return ((letter, letter,) for letter in string.ascii_uppercase) def queryset(self, request, queryset): - """ - Returns the filtered queryset based on the value - provided in the query string and retrievable via - `self.value()`. - """ if self.value(): - return queryset.filter((self.parameter_name + '__istartswith', self.value())) + return queryset.filter( + (self.parameter_name + '__istartswith', self.value()) + ) + +class RangeFilter(admin.FieldListFilter): + num_faixas = 4 + parameter_name = None + + def __init__(self, field, request, params, model, model_admin, field_path): + self.model = model + self.model_admin = model_admin + self.parameter_name = f'{field_path}__range' + + super().__init__(field, request, params, model, model_admin, field_path) + + if self.parameter_name in params: + value = params.pop(self.parameter_name) + self.used_parameters[self.parameter_name] = value + lookup_choices = self.lookups(request, model_admin) + + if lookup_choices is None: + lookup_choices = () + self.lookup_choices = list(lookup_choices) + + def ranges(self, model): + tudo = model.objects.values_list(self.field_path, flat=True).order_by( + self.field_path) + passo = len(tudo) // self.num_faixas + ultimo = 0 + + for i in range(1, self.num_faixas): + yield (i, ultimo, tudo[i*passo]) + ultimo = tudo[i*passo] + + yield (self.num_faixas, ultimo, tudo.last()) + + def lookups(self, request, model_admin): + return ((value, _(f"de {min} até {max}")) + for value, min, max in self.ranges(self.model)) + + def has_output(self): + return self.model.objects.exists() + + def value(self): + return self.used_parameters.get(self.parameter_name) + + def expected_parameters(self): + return [self.parameter_name,] + + def choices(self, changelist): + yield { + 'selected': self.value() is None, + 'query_string': changelist.get_query_string( + remove=[self.parameter_name]), + 'display': _('All'), + } + for lookup, title in self.lookup_choices: + yield { + 'selected': self.value() == str(lookup), + 'query_string': changelist.get_query_string( + {self.parameter_name: lookup} + ), + 'display': title, + } + + def queryset(self, request, queryset): + try: + for value, min, max in self.ranges(self.model): + if self.value() == str(value): + return queryset.filter( + (f'{self.field_path}__gte', min), + (f'{self.field_path}__lt', max) + ) + except (ValueError, ValidationError) as e: + raise IncorrectLookupParameters(e) + + return queryset \ No newline at end of file diff --git a/sigi/settings/base.py b/sigi/settings/base.py index 3464552..85289e1 100644 --- a/sigi/settings/base.py +++ b/sigi/settings/base.py @@ -20,6 +20,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent INSTALLED_APPS = [ 'sigi.apps.servidores', + 'sigi.apps.contatos', 'django_bootstrap5', 'django.forms', 'django.contrib.admin',