diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt new file mode 100644 index 0000000..7246f4a --- /dev/null +++ b/requirements/dev-requirements.txt @@ -0,0 +1,2 @@ +-r requirements.txt +django-debug-toolbar==3.2.4 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index ae2ca5a..5ae955a 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -2,3 +2,5 @@ ipython==7.30.1 Django==4.0.1 django-extensions==3.1.5 psycopg2==2.9.3 +django-bootstrap5==21.3 +Pillow==9.0.0 diff --git a/sigi/apps/servidores/admin.py b/sigi/apps/servidores/admin.py index 585906c..18097e1 100644 --- a/sigi/apps/servidores/admin.py +++ b/sigi/apps/servidores/admin.py @@ -1,45 +1,22 @@ -# -*- coding: utf-8 -*- +from django.db import models from django.contrib import admin -from django.contrib.contenttypes import generic +from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ - -from sigi.apps.contatos.models import Endereco, Telefone from sigi.apps.servidores.models import Servidor, Servico -from sigi.apps.utils.admin_widgets import AdminImageWidget -from sigi.apps.utils.base_admin import BaseModelAdmin -from sigi.apps.utils.filters import AlphabeticFilter - -class ServidorFilter(AlphabeticFilter): - title = _('Nome do Servidor') - parameter_name = 'servidor__nome_completo' - -class ServicoFilter(admin.SimpleListFilter): - title = _("Subordinados à") - parameter_name = 'subordinado__id__exact' - - def lookups(self, request, model_admin): - return ([('None', _("Nenhum"))] + - [(s.id, s.nome) for s in Servico.objects.exclude(servico=None)]) - - def queryset(self, request, queryset): - if self.value(): - if self.value() == "None": - queryset = queryset.filter(subordinado=None) - else: - queryset = queryset.filter(subordinado__id=self.value()) - return queryset +from sigi.apps.servidores.filters import ServicoFilter class ServicoInline(admin.TabularInline): model = Servico fields = ['nome', 'sigla', 'responsavel',] + autocomplete_fields = ['responsavel',] class ServidorInline(admin.TabularInline): model = Servidor fields = ('imagem_foto', 'nome_completo', 'is_active', ) readonly_fields = ('imagem_foto', 'nome_completo', 'is_active', ) - def has_add_permission(self, request): + def has_add_permission(self, request, obj): return False def has_delete_permission(self, request, obj): @@ -47,11 +24,13 @@ class ServidorInline(admin.TabularInline): def imagem_foto(sels, servidor): if servidor.foto: - return ''.format(url=servidor.foto.url) + return mark_safe( + f'' + ) else: return "" imagem_foto.short_description = _("foto") - imagem_foto.allow_tags = True def is_active(self, servidor): if servidor.user: @@ -62,19 +41,19 @@ class ServidorInline(admin.TabularInline): is_active.boolean = True is_active.short_description = _('ativo') - @admin.register(Servico) class ServicoAdmin(admin.ModelAdmin): list_display = ['sigla', 'nome', 'subordinado', 'responsavel'] list_filter = [ServicoFilter,] search_fields = ['nome', 'sigla',] + autocomplete_fields = ['subordinado', 'responsavel',] inlines = [ServicoInline, ServidorInline,] @admin.register(Servidor) -class ServidorAdmin(BaseModelAdmin): +class ServidorAdmin(admin.ModelAdmin): list_display = ('imagem_foto', 'nome_completo', 'is_active', 'servico', ) list_display_links = ('imagem_foto', 'nome_completo',) - list_filter = ('user__is_active', 'externo', 'servico') + list_filter = ('user__is_active', 'externo', 'servico',) search_fields = ('nome_completo', 'user__email', 'user__first_name', 'user__last_name', 'user__username', 'servico__nome', 'servico__sigla') @@ -92,16 +71,6 @@ class ServidorAdmin(BaseModelAdmin): return super(ServidorAdmin, self).lookup_allowed(lookup, value) or \ lookup in ['user__is_active__exact'] - # def has_add_permission(self, request): - # return False - - def formfield_for_dbfield(self, db_field, **kwargs): - if db_field.name == 'foto': - request = kwargs.pop("request", None) - kwargs['widget'] = AdminImageWidget - return db_field.formfield(**kwargs) - return super(ServidorAdmin, self).formfield_for_dbfield(db_field, **kwargs) - def is_active(self, servidor): if servidor.user: return servidor.user.is_active @@ -113,8 +82,10 @@ class ServidorAdmin(BaseModelAdmin): def imagem_foto(sels, servidor): if servidor.foto: - return ''.format(url=servidor.foto.url) + return mark_safe( + f'' + ) else: return "" - imagem_foto.short_description = _("foto") - imagem_foto.allow_tags = True \ No newline at end of file + imagem_foto.short_description = _("foto") \ No newline at end of file diff --git a/sigi/apps/servidores/filters.py b/sigi/apps/servidores/filters.py new file mode 100644 index 0000000..bc4841a --- /dev/null +++ b/sigi/apps/servidores/filters.py @@ -0,0 +1,20 @@ +from django.contrib import admin +from django.utils.translation import gettext as _ +from sigi.apps.servidores.models import Servico + + +class ServicoFilter(admin.SimpleListFilter): + title = _("Subordinados à") + parameter_name = 'subordinado__id__exact' + + def lookups(self, request, model_admin): + return ([('None', _("Nenhum"))] + + [(s.id, s.nome) for s in Servico.objects.exclude(servico=None)]) + + def queryset(self, request, queryset): + if self.value(): + if self.value() == "None": + queryset = queryset.filter(subordinado=None) + else: + queryset = queryset.filter(subordinado__id=self.value()) + return queryset diff --git a/sigi/apps/servidores/forms.py b/sigi/apps/servidores/forms.py deleted file mode 100644 index 773b143..0000000 --- a/sigi/apps/servidores/forms.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -from collections import namedtuple - -from django import forms -from django.utils.translation import gettext as _ - -from sigi.apps.servidores.models import Ferias, Licenca, Funcao, Servidor - - -def valida_data_inicial_menor_que_final(data, chave_ini, chave_fim): - if data.get(chave_ini) >= data.get(chave_fim): - raise forms.ValidationError(_( - "A data de início deve ser menor que a data final. Verifique novamente")) - - -class FeriasForm(forms.ModelForm): - - class Meta: - model = Ferias - fields = '__all__' - - def clean(self): - data = self.cleaned_data - valida_data_inicial_menor_que_final(data, 'inicio_ferias', 'fim_ferias') - return data - - -class LicencaForm(forms.ModelForm): - - class Meta: - model = Licenca - fields = '__all__' - - def clean(self): - data = self.cleaned_data - valida_data_inicial_menor_que_final(data, 'inicio_licenca', 'fim_licenca') - return data - - -Periodo = namedtuple('Periodo', ['ini', 'fim']) - - -def periodos_se_sobrepoe(periodo1, periodo2): - return not (periodo1.fim < periodo2.ini or periodo2.fim < periodo1.ini) - - -class FuncaoForm(forms.ModelForm): - - class Meta: - model = Funcao - fields = '__all__' - - def clean(self): - data = self.cleaned_data - valida_data_inicial_menor_que_final(data, 'inicio_funcao', 'fim_funcao') - - # Verifica na função anterior, se o seu período é igual - # ou está entre o período da função atual. - servidor = Servidor.objects.get(nome_completo=data.get('servidor')) - for funcao in servidor.funcao_set.all(): - if periodos_se_sobrepoe( - Periodo(funcao.inicio_funcao, funcao.fim_funcao), - Periodo(data.get('inicio_funcao'), data.get('fim_funcao'))): - raise forms.ValidationError(_( - "Este período coincide com o de outra função exercida.")) - return data diff --git a/sigi/apps/servidores/migrations/0001_initial.py b/sigi/apps/servidores/migrations/0001_initial.py index cae4cc6..edc31a1 100644 --- a/sigi/apps/servidores/migrations/0001_initial.py +++ b/sigi/apps/servidores/migrations/0001_initial.py @@ -97,8 +97,8 @@ class Migration(migrations.Migration): ('apontamentos', models.TextField(null=True, verbose_name='apontamentos', blank=True)), ('email_pessoal', models.EmailField(max_length=75, null=True, verbose_name=b'email pessoal', blank=True)), ('ramal', models.CharField(max_length=25, null=True, blank=True)), - ('servico', models.ForeignKey(blank=True, to='servidores.Servico', null=True)), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, unique=True)), + ('servico', models.ForeignKey(blank=True, to='servidores.Servico', null=True, on_delete=models.CASCADE)), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, unique=True, on_delete=models.CASCADE)), ], options={ 'ordering': ('nome_completo',), @@ -112,7 +112,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('nome', models.CharField(max_length=250, null=True)), ('sigla', models.CharField(max_length=10, null=True)), - ('responsavel', models.ForeignKey(related_name=b'diretor', to='servidores.Servidor', null=True)), + ('responsavel', models.ForeignKey(related_name=b'diretor', to='servidores.Servidor', null=True, on_delete=models.CASCADE)), ], options={ 'ordering': ('nome',), @@ -122,31 +122,31 @@ class Migration(migrations.Migration): migrations.AddField( model_name='servico', name='responsavel', - field=models.ForeignKey(related_name=b'chefe', to='servidores.Servidor', null=True), + field=models.ForeignKey(related_name=b'chefe', to='servidores.Servidor', null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='servico', name='subsecretaria', - field=models.ForeignKey(to='servidores.Subsecretaria', null=True), + field=models.ForeignKey(to='servidores.Subsecretaria', null=True, on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='licenca', name='servidor', - field=models.ForeignKey(to='servidores.Servidor'), + field=models.ForeignKey(to='servidores.Servidor', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='funcao', name='servidor', - field=models.ForeignKey(to='servidores.Servidor'), + field=models.ForeignKey(to='servidores.Servidor', on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( model_name='ferias', name='servidor', - field=models.ForeignKey(to='servidores.Servidor'), + field=models.ForeignKey(to='servidores.Servidor', on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/sigi/apps/servidores/migrations/0006_auto_20210429_0822.py b/sigi/apps/servidores/migrations/0006_auto_20210429_0822.py index 72737ed..d50e2cb 100644 --- a/sigi/apps/servidores/migrations/0006_auto_20210429_0822.py +++ b/sigi/apps/servidores/migrations/0006_auto_20210429_0822.py @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='servidor', name='user', - field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True), + field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE), preserve_default=True, ), ] diff --git a/sigi/apps/servidores/models.py b/sigi/apps/servidores/models.py index 9b8fdc3..1c599fa 100644 --- a/sigi/apps/servidores/models.py +++ b/sigi/apps/servidores/models.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from django.contrib.auth.models import User from django.db import models from django.db.models.signals import post_save, pre_save @@ -27,8 +26,8 @@ class Servico(models.Model): verbose_name = _('serviço') verbose_name_plural = _('serviços') - def __unicode__(self): - return "{sigla} - {nome}".format(sigla=self.sigla, nome=self.nome) + def __str__(self): + return f"{self.sigla} - {self.nome}" class Servidor(models.Model): user = models.ForeignKey( @@ -64,7 +63,7 @@ class Servidor(models.Model): ordering = ('nome_completo',) verbose_name_plural = 'servidores' - def __unicode__(self): + def __str__(self): return self.nome_completo def save(self, *args, **kwargs): @@ -80,7 +79,7 @@ User.servidor = property(lambda user: Servidor.objects.get(user=user) else None) # Sinal para ao criar um usuário criar um servidor -# baseado no nome contino no LDAP +# baseado no nome contido no LDAP def create_user_profile(sender, instance, created, **kwargs): if created: Servidor.objects.create( diff --git a/sigi/apps/servidores/templates/admin/servidores/servidor/change_list.html b/sigi/apps/servidores/templates/admin/servidores/servidor/change_list.html deleted file mode 100644 index 0ed52dc..0000000 --- a/sigi/apps/servidores/templates/admin/servidores/servidor/change_list.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends 'admin/change_list.html' %} -{% load i18n reporting_tags %} - -{% block object-tools-items %} -
  • - - {% trans 'Relatório por cargo' %} -
  • -
  • - - {% trans 'Relatório por função' %} -
  • - {{ block.super }} -{% endblock %} diff --git a/sigi/apps/servidores/templates/admin/widgets/clearable_file_input.html b/sigi/apps/servidores/templates/admin/widgets/clearable_file_input.html new file mode 100644 index 0000000..82f8e8e --- /dev/null +++ b/sigi/apps/servidores/templates/admin/widgets/clearable_file_input.html @@ -0,0 +1,18 @@ + +{% if widget.is_initial %} +

    + {{ widget.initial_text }}: + {{ widget.value }} + {% if not widget.required %} + + + + + {% endif %} +
    + {{ widget.input_text }}: +{% endif %} + +{% if widget.is_initial %} +

    +{% endif %} diff --git a/sigi/apps/servidores/test_servidores_forms.py b/sigi/apps/servidores/test_servidores_forms.py deleted file mode 100644 index cbbd838..0000000 --- a/sigi/apps/servidores/test_servidores_forms.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -from django import forms -import pytest -from datetime import date - -from sigi.apps.servidores.forms import valida_data_inicial_menor_que_final, Periodo, periodos_se_sobrepoe - - -@pytest.mark.parametrize('data', [ - dict(ini=1, fim=2), - pytest.mark.xfail(raises=forms.ValidationError)(dict(ini=2, fim=1)), - pytest.mark.xfail(raises=forms.ValidationError)(dict(ini=1, fim=1)), -]) -def test_valida_data_inicial_menor_que_final(data): - valida_data_inicial_menor_que_final(data, 'ini', 'fim') - - -periodos = [ - [Periodo(date(2000, 10, 1), date(2001, 1, 1)), Periodo(date(2001, 1, 1), date(2002, 2, 2)), True], # um dia de interseção - [Periodo(date(2000, 10, 1), date(2001, 1, 1)), Periodo(date(2001, 1, 2), date(2002, 2, 2)), False], # exatamente um dia após - [Periodo(date(2000, 10, 1), date(2001, 1, 1)), Periodo(date(2000, 12, 2), date(2002, 2, 2)), True], - [Periodo(date(2000, 10, 1), date(2001, 1, 1)), Periodo(date(2014, 1, 1), date(2014, 2, 2)), False], -] - -# para testar que a ordem dos parametros nao importa -periodos_trocados = [[b, a, res] for [a, b, res] in periodos] - - -@pytest.mark.parametrize('periodo1, periodo2, resultado', periodos + periodos_trocados) -def test_periodos_se_sobrepoe(periodo1, periodo2, resultado): - assert periodos_se_sobrepoe(periodo1, periodo2) == resultado diff --git a/sigi/apps/utils/admin_widgets.py b/sigi/apps/utils/admin_widgets.py deleted file mode 100644 index 7c9fc47..0000000 --- a/sigi/apps/utils/admin_widgets.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.contrib.admin.widgets import AdminFileWidget -from django.utils.safestring import mark_safe -from django.utils.translation import gettext as _ - - -class AdminImageWidget(AdminFileWidget): - - def render(self, name, value, attrs=None): - output = [] - if value and getattr(value, "url", None): - image_url = value.url - file_name = str(value) - output.append( - ''' %s
    %s''' % - (image_url, image_url, file_name, _('Change') + ':')) - output.append(super(AdminFileWidget, self).render(name, value, attrs)) - return mark_safe(''.join(output)) diff --git a/sigi/settings/base.py b/sigi/settings/base.py index eafa794..b02ca8c 100644 --- a/sigi/settings/base.py +++ b/sigi/settings/base.py @@ -19,13 +19,15 @@ BASE_DIR = Path(__file__).resolve().parent.parent # Application definition INSTALLED_APPS = [ + 'sigi.apps.servidores', + 'django_bootstrap5', + 'django.forms', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'sigi.apps.servidores', ] MIDDLEWARE = [ @@ -43,7 +45,7 @@ ROOT_URLCONF = 'sigi.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [BASE_DIR / 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -56,20 +58,26 @@ TEMPLATES = [ }, ] +FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' + WSGI_APPLICATION = 'sigi.wsgi.application' # Internationalization # https://docs.djangoproject.com/en/4.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'pt-br' -TIME_ZONE = 'UTC' +TIME_ZONE = "America/Sao_Paulo" USE_I18N = True +USE_L10N = True + USE_TZ = True +USE_THOUSAND_SEPARATOR = True + # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.0/howto/static-files/ diff --git a/sigi/settings/development.py b/sigi/settings/development.py index 9efc685..e7f3df7 100644 --- a/sigi/settings/development.py +++ b/sigi/settings/development.py @@ -11,6 +11,13 @@ DEBUG = True ALLOWED_HOSTS = [] +INTERNAL_IPS = ["127.0.0.1",] + +# Application definition + +INSTALLED_APPS = ['debug_toolbar',] + INSTALLED_APPS + +MIDDLEWARE = ['debug_toolbar.middleware.DebugToolbarMiddleware',] + MIDDLEWARE # Database # https://docs.djangoproject.com/en/4.0/ref/settings/#databases diff --git a/templates/base_change_form.html b/sigi/templates/base_change_form.html similarity index 100% rename from templates/base_change_form.html rename to sigi/templates/base_change_form.html diff --git a/sigi/urls.py b/sigi/urls.py index fd93220..40febb3 100644 --- a/sigi/urls.py +++ b/sigi/urls.py @@ -14,8 +14,14 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import path, include +from django.conf import settings urlpatterns = [ path('admin/', admin.site.urls), ] + +if settings.DEBUG: + urlpatterns = urlpatterns + [ + path('__debug__/', include('debug_toolbar.urls')) + ]