diff --git a/sigi/apps/convenios/migrations/0020_gescon_orgaos_gestores.py b/sigi/apps/convenios/migrations/0020_gescon_orgaos_gestores.py new file mode 100644 index 0000000..22adc43 --- /dev/null +++ b/sigi/apps/convenios/migrations/0020_gescon_orgaos_gestores.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.3 on 2022-04-20 13:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('convenios', '0019_alter_anexo_arquivo_alter_anexo_descricao_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='gescon', + name='orgaos_gestores', + field=models.TextField(default='SCCO', help_text='Siglas de órgãos gestores que devem aparecer no campoORGAOSGESTORESTITULARES', verbose_name='Órgãos gestores'), + ), + ] diff --git a/sigi/apps/convenios/models.py b/sigi/apps/convenios/models.py index c3e5736..6e450d7 100644 --- a/sigi/apps/convenios/models.py +++ b/sigi/apps/convenios/models.py @@ -381,6 +381,15 @@ class Gescon(models.Model): "
  • Ocorrendo qualquer uma das palavras, o contrato será " "importado.
  • ") ) + orgaos_gestores = models.TextField( + _("Órgãos gestores"), + default="SCCO", + help_text=_("Siglas de órgãos gestores que devem aparecer no campo" + "ORGAOSGESTORESTITULARES" + "") + ) email = models.EmailField( _("E-mail"), help_text=_("Caixa de e-mail para onde o relatório diário de " @@ -429,16 +438,13 @@ class Gescon(models.Model): def importa_contratos(self): self.ultima_importacao = "" self.add_message( - _("Importação iniciada em {:%d/%m/%Y %H:%M:%S}\n" - "==========================================\n").format( - datetime.now() - ) + _(f"Importação iniciada em {datetime.now():%d/%m/%Y %H:%M:%S}\n" + "==========================================================\n") ) - if self.palavras == "": - self.add_message(_("Nenhuma palavra de pesquisa definida - " - "processo abortado."), True) - return + if self.palavras == "" or self.orgaos_gestores == "": + self.add_message(_("Nenhuma palavra de pesquisa ou orgãos " + "gestores definidos - processo abortado."), True) if self.subespecies == "": self.add_message(_("Nenhuma subespécie definida - processo " @@ -457,11 +463,14 @@ class Gescon(models.Model): return palavras = self.palavras.split() + orgaos = self.orgaos_gestores.split() subespecies = {tuple(s.split("=")) for s in self.subespecies.split()} + lista_cnpj = {re.sub("[^\d]", "", o.cnpj).zfill(14): o + for o in Orgao.objects.exclude(cnpj="") + if re.sub("[^\d]", "", o.cnpj) != ''} for sigla_gescon, sigla_sigi in subespecies: - self.add_message(_("\nImportando subespécie {s}".format( - s=sigla_gescon))) + self.add_message(_(f"\nImportando subespécie {sigla_gescon}")) url = self.url_gescon.format(s=sigla_gescon) projeto = Projeto.objects.get(sigla=sigla_sigi) @@ -469,42 +478,34 @@ class Gescon(models.Model): try: response = requests.get(url, verify=False) except Exception as e: - self.add_message( - _("\tErro ao acessar {url}: {errmsg}").format( - url=url, - errmsg=e.message.decode("utf8") - ) - ) + self.add_message(_(f"\tErro ao acessar {url}: {e.message}")) continue if not response.ok: self.add_message( - _("\tErro ao acessar {url}: {reason}").format( - url=url, - reason=response.reason - ) + _(f"\tErro ao acessar {url}: {response.reason}") ) continue if not 'application/json' in response.headers.get('Content-Type'): - self.add_message(_("\tResultado da consulta à {url} não " - "retornou dados em formato json").format( - url=url - ) - ) + self.add_message(_(f"\tResultado da consulta à {url} não " + "retornou dados em formato json")) continue contratos = response.json() # Pegar só os contratos que possuem alguma das palavras-chave - nossos = [c for c in contratos - if any(palavra in c['objeto'] for palavra in palavras)] + nossos = [ + c for c in contratos + if any(palavra in c['objeto'] for palavra in palavras) or + any(orgao in c['orgaosGestoresTitulares'] + for orgao in orgaos + if c['orgaosGestoresTitulares'] is not None) + ] self.add_message( - _("\t{count} contratos encontrados no Gescon").format( - count=len(nossos) - ) + _(f"\t{len(nossos)} contratos encontrados no Gescon") ) novos = 0 @@ -514,17 +515,14 @@ class Gescon(models.Model): for contrato in nossos: numero = contrato['numero'].zfill(8) - numero = "{}/{}".format(numero[:4], numero[4:]) + numero = f"{numero[:4]}/{numero[4:]}" sigad = contrato['processo'].zfill(17) - sigad = "{}.{}/{}-{}".format(sigad[:5], sigad[5:11], - sigad[11:15], sigad[15:]) - + sigad = f"{sigad[:5]}.{sigad[5:11]}/{sigad[11:15]}-{sigad[15:]}" if contrato['cnpjCpfFornecedor']: cnpj = contrato['cnpjCpfFornecedor'].zfill(14) - cnpj = "{}.{}.{}/{}-{}".format(cnpj[:2], cnpj[2:5], - cnpj[5:8], cnpj[8:12], - cnpj[12:]) + cnpj_masked = (f"{cnpj[:2]}.{cnpj[2:5]}.{cnpj[5:8]}/" + f"{cnpj[8:12]}-{cnpj[12:]}") else: cnpj = None @@ -541,8 +539,8 @@ class Gescon(models.Model): if (cnpj is None) and (nome is None): self.add_message( - _("\tO contrato {numero} no Gescon não informa o CNPJ " - "nem o nome do órgão.").format(numero=numero) + _(f"\tO contrato {numero} no Gescon não informa o CNPJ" + "nem o nome do órgão.") ) erros += 1 continue @@ -550,13 +548,16 @@ class Gescon(models.Model): orgao = None if cnpj is not None: - try: - orgao = Orgao.objects.get(cnpj=cnpj) - except ( - Orgao.DoesNotExist, - Orgao.MultipleObjectsReturned) as e: - orgao = None - pass + if cnpj in lista_cnpj: + orgao = lista_cnpj[cnpj] + else: + try: + orgao = Orgao.objects.get(cnpj=cnpj_masked) + except ( + Orgao.DoesNotExist, + Orgao.MultipleObjectsReturned) as e: + orgao = None + pass if (orgao is None) and (nome is not None): try: @@ -569,14 +570,11 @@ class Gescon(models.Model): if orgao is None: self.add_message( - _("\tÓrgão não encontrado no SIGI ou mais de um órgão" - "encontrado com o mesmo CNPJ ou nome. Favor " - "regularizar o cadastro: CNPJ: {cnpj}, " - "Nome: {nome}".format( - cnpj=contrato['cnpjCpfFornecedor'], - nome=contrato['nomeFornecedor'] - ) - ) + _(f"\tÓrgão não encontrado no SIGI ou mais de um órgão" + f"encontrado com o mesmo CNPJ ou nome. Favor " + f"regularizar o cadastro: " + f"CNPJ: {contrato['cnpjCpfFornecedor']}, " + f"Nome: {contrato['nomeFornecedor']}") ) erros += 1 continue @@ -628,15 +626,9 @@ class Gescon(models.Model): convenio.observacao_gescon = '' if convenio.casa_legislativa != orgao: self.add_message( - _("\tO órgao no convênio {url} diverge do que " - "consta no Gescon ({cnpj}, {nome})").format( - url=reverse('admin:%s_%s_change' % ( - convenio._meta.app_label, - convenio._meta.model_name), - args=[convenio.id]), - cnpj=cnpj, - nome=contrato['nomeFornecedor'] - ) + _(f"\tO órgao no convênio {convenio.id} diverge do " + f"que consta no Gescon ({cnpj}, " + f"{contrato['nomeFornecedor']})") ) convenio.observacao_gescon = _( 'ERRO: Órgão diverge do Gescon. Não atualizado!' @@ -647,19 +639,12 @@ class Gescon(models.Model): if convenio.num_processo_sf != sigad: self.add_message( - _("\tO contrato Gescon nº {numero} corresponde" - " ao convênio SIGI {url}, mas o NUP sigad " - "diverge (Gescon: {sigad_gescon}, " - "SIGI: {sigad_sigi}). CORRIGIDO!").format( - numero=numero, - url=reverse('admin:%s_%s_change' % ( - convenio._meta.app_label, - convenio._meta.model_name), - args=[convenio.id]), - sigad_gescon=sigad, - sigad_sigi=convenio.num_processo_sf - ) - ) + _(f"\tO contrato Gescon nº {numero} corresponde" + f" ao convênio SIGI {convenio.id}, mas o NUP " + f"sigad diverge (Gescon: {sigad}, " + f"SIGI: {convenio.num_processo_sf}). " + "CORRIGIDO!") + ) convenio.num_processo_sf = sigad convenio.observacao_gescon += _( "Número do SIGAD atualizado.\n" @@ -668,19 +653,11 @@ class Gescon(models.Model): if convenio.num_convenio != numero: self.add_message( - _("\tO contrato Gescon ID {id} corresponde ao " - "convênio SIGI {url}, mas o número do convênio" - " diverge (Gescon: {numero_gescon}, SIGI: " - "{numero_sigi}). CORRIGIDO!").format( - id=contrato['id'], - url=reverse('admin:%s_%s_change' % ( - convenio._meta.app_label, - convenio._meta.model_name), - args=[convenio.id] - ), - numero_gescon=numero, - numero_sigi=convenio.num_convenio - ) + _(f"\tO contrato Gescon ID {contrato['id']} " + f"corresponde ao convênio SIGI {convenio.id}, " + "mas o número do convênio diverge (" + f"Gescon: {numero}, SIGI: {convenio.num_convenio}" + "). CORRIGIDO!") ) convenio.num_convenio = numero convenio.observacao_gescon += _( @@ -713,44 +690,28 @@ class Gescon(models.Model): convenio.save() except Exception as e: self.add_message( - _("Ocorreu um erro ao salvar o convênio {url} no " - "SIGI. Alguma informação do Gescon pode ter " - "quebrado o sistema. Informe ao suporte. Erro:" - "{errmsg}").format( - url=reverse('admin:%s_%s_change' % ( - convenio._meta.app_label, - convenio._meta.model_name), - args=[convenio.id] - ), - errmsg=e.message.decode("utf8") - ) + _("Ocorreu um erro ao salvar o convênio " + f"{convenio.id} no SIGI. Alguma informação do " + "Gescon pode ter quebrado o sistema. Informe ao " + f"suporte. Erro: {e.message}") ) erros += 1 continue atualizados += 1 else: - self.add_message(_("\tExistem {count} convênios no SIGI " - "que correspondem ao mesmo contrato no " - "Gescon (contrato {numero}, sigad " - "{sigad})").format( - count=chk, - numero=numero, - sigad=sigad - ) + self.add_message( + _(f"\tExistem {chk} convênios no SIGI que " + "correspondem ao mesmo contrato no Gescon (contrato " + f"{numero}, sigad {sigad})") ) erros += 1 continue self.add_message( - _("\t{novos} novos convenios adicionados ao SIGI, " - "{atualizados} atualizados, sendo {alertas} com alertas, e " - "{erros} reportados com erro.").format( - novos=novos, - atualizados=atualizados, - alertas=alertas, - erros=erros - ) + _(f"\t{novos} novos convenios adicionados ao SIGI, " + f"{atualizados} atualizados, sendo {alertas} com alertas, e " + f"{erros} reportados com erro.") ) self.save() diff --git a/sigi/apps/eventos/admin.py b/sigi/apps/eventos/admin.py index 0656646..2099770 100644 --- a/sigi/apps/eventos/admin.py +++ b/sigi/apps/eventos/admin.py @@ -1,11 +1,54 @@ from django.contrib import admin from django.http import HttpResponseRedirect from django.utils.translation import gettext as _ +from import_export.fields import Field from tinymce.models import HTMLField from tinymce.widgets import AdminTinyMCE from sigi.apps.eventos.models import (ModeloDeclaracao, Modulo, TipoEvento, Funcao, Evento, Equipe, Convite, Anexo) from sigi.apps.eventos.forms import EventoAdminForm +from sigi.apps.utils.filters import EmptyFilter +from sigi.apps.utils.mixins import CartExportMixin, ValueLabeledResource + +class EventoResource(ValueLabeledResource): + # categoria_evento = Field(column_name="tipo_evento__categoria") + # status = Field(column_name="status") + class Meta: + model = Evento + fields = ( + 'id', 'tipo_evento__nome', 'tipo_evento__categoria', 'nome', + 'descricao', 'virtual', 'solicitante', 'num_processo', + 'data_pedido', 'data_inicio', 'data_termino', 'carga_horaria', + 'casa_anfitria__nome', 'casa_anfitria__logradouro', + 'casa_anfitria__bairro', 'casa_anfitria__municipio__nome', + 'casa_anfitria__municipio__uf__sigla', 'casa_anfitria__cep', + 'casa_anfitria__email', 'local', 'municipio__nome', + 'municipio__uf__sigla', 'observacao', 'publico_alvo', + 'total_participantes', 'status', 'data_cancelamento', + 'motivo_cancelamento', 'equipe__membro__nome_completo', + 'equipe__funcao__nome', 'convite__casa__nome', + 'convite__casa__municipio__nome', + 'convite__casa__municipio__uf__sigla', 'convite__casa__cep', + 'convite__casa__email', 'convite__aceite', 'convite__data_convite', + 'convite__participou', 'convite__qtde_participantes', + 'convite__nomes_participantes', + ) + export_order = fields + + def dehydrate_tipo_evento__categoria(self, obj): + return dict(TipoEvento.CATEGORIA_CHOICES)[obj['tipo_evento__categoria']] + + def dehydrate_virtual(self, obj): + return "Sim" if obj['virtual'] else "Não" + + def dehydrate_status(self, obj): + return dict(Evento.STATUS_CHOICES)[obj['status']] + + def dehydrate_convite__aceite(self, obj): + return "Sim" if obj['convite__aceite'] else "Não" + + def dehydrate_convite__participou(self, obj): + return "Sim" if obj['convite__participou'] else "Não" @admin.register(TipoEvento) class TipoEventAdmin(admin.ModelAdmin): @@ -36,18 +79,28 @@ class AnexoInline(admin.StackedInline): exclude = ('data_pub',) @admin.register(Evento) -class EventoAdmin(admin.ModelAdmin): +class EventoAdmin(CartExportMixin, admin.ModelAdmin): form = EventoAdminForm + resource_class = EventoResource date_hierarchy = 'data_inicio' - list_display = ('nome', 'tipo_evento', 'status', 'data_inicio', - 'data_termino', 'municipio', 'solicitante', + list_display = ('nome', 'tipo_evento', 'status', 'link_sigad', + 'data_inicio', 'data_termino', 'municipio', 'solicitante', 'total_participantes',) - list_filter = ('status', 'tipo_evento', 'tipo_evento__categoria', 'virtual', - 'municipio__uf', 'solicitante') + list_filter = ('status', ('num_processo', EmptyFilter), 'tipo_evento', + 'tipo_evento__categoria', 'virtual', 'municipio__uf', + 'solicitante') raw_id_fields = ('casa_anfitria', 'municipio',) search_fields = ('nome', 'tipo_evento__nome', 'casa_anfitria__search_text', 'municipio__search_text', 'solicitante') inlines = (EquipeInline, ConviteInline, ModuloInline, AnexoInline) + save_as = True + + def link_sigad(self, obj): + if obj.pk is None: + return "" + return obj.get_sigad_url() + link_sigad.short_description = _(u"número do processo SIGAD") + link_sigad.allow_tags = True def lookup_allowed(self, lookup, value): return (super(EventoAdmin, self).lookup_allowed(lookup, value) or diff --git a/sigi/apps/eventos/forms.py b/sigi/apps/eventos/forms.py index b95d7d4..e100412 100644 --- a/sigi/apps/eventos/forms.py +++ b/sigi/apps/eventos/forms.py @@ -6,10 +6,10 @@ class EventoAdminForm(forms.ModelForm): class Meta: model = Evento fields = ('tipo_evento', 'nome', 'descricao', 'virtual', 'solicitante', - 'data_inicio', 'data_termino', 'carga_horaria', - 'casa_anfitria', 'municipio', 'local', 'publico_alvo', - 'total_participantes', 'status', 'data_cancelamento', - 'motivo_cancelamento', ) + 'num_processo', 'data_pedido', 'data_inicio', 'data_termino', + 'carga_horaria', 'casa_anfitria', 'municipio', 'observacao', + 'local', 'publico_alvo', 'total_participantes', 'status', + 'data_cancelamento', 'motivo_cancelamento', ) def clean(self): cleaned_data = super(EventoAdminForm, self).clean() diff --git a/sigi/apps/eventos/migrations/0018_evento_data_pedido_evento_num_processo_and_more.py b/sigi/apps/eventos/migrations/0018_evento_data_pedido_evento_num_processo_and_more.py new file mode 100644 index 0000000..8a5751b --- /dev/null +++ b/sigi/apps/eventos/migrations/0018_evento_data_pedido_evento_num_processo_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.0.3 on 2022-04-19 12:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('eventos', '0017_alter_anexo_arquivo_alter_anexo_descricao_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='evento', + name='data_pedido', + field=models.DateField(blank=True, help_text='Data em que o pedido do Gabinete chegou à COPERI', null=True, verbose_name='Data do pedido'), + ), + migrations.AddField( + model_name='evento', + name='num_processo', + field=models.CharField(blank=True, help_text='Formato:XXXXX.XXXXXX/XXXX-XX', max_length=20, verbose_name='número do processo SIGAD'), + ), + migrations.AddField( + model_name='evento', + name='observacao', + field=models.TextField(blank=True, verbose_name='Observações e anotações'), + ), + migrations.AlterField( + model_name='evento', + name='status', + field=models.CharField(choices=[('E', 'Em planejamento'), ('G', 'Aguardando abertura SIGAD'), ('P', 'Previsão'), ('A', 'A confirmar'), ('O', 'Confirmado'), ('R', 'Realizado'), ('C', 'Cancelado')], max_length=1, verbose_name='Status'), + ), + ] diff --git a/sigi/apps/eventos/migrations/0019_alter_evento_status.py b/sigi/apps/eventos/migrations/0019_alter_evento_status.py new file mode 100644 index 0000000..dd870eb --- /dev/null +++ b/sigi/apps/eventos/migrations/0019_alter_evento_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.3 on 2022-04-20 13:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('eventos', '0018_evento_data_pedido_evento_num_processo_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='evento', + name='status', + field=models.CharField(choices=[('E', 'Em planejamento'), ('G', 'Aguardando abertura SIGAD'), ('P', 'Previsão'), ('A', 'A confirmar'), ('O', 'Confirmado'), ('R', 'Realizado'), ('C', 'Cancelado'), ('Q', 'Arquivado')], max_length=1, verbose_name='Status'), + ), + ] diff --git a/sigi/apps/eventos/models.py b/sigi/apps/eventos/models.py index aaddd83..85902d1 100644 --- a/sigi/apps/eventos/models.py +++ b/sigi/apps/eventos/models.py @@ -1,3 +1,4 @@ +import re from datetime import datetime from django.db import models from django.db.models import Sum @@ -33,11 +34,14 @@ class TipoEvento(models.Model): class Evento(models.Model): STATUS_CHOICES = ( + ('E', _(u"Em planejamento")), + ('G', _(u"Aguardando abertura SIGAD")), ('P', _("Previsão")), ('A', _("A confirmar")), ('O', _("Confirmado")), ('R', _("Realizado")), - ('C', _("Cancelado")) + ('C', _("Cancelado")), + ('Q', _("Arquivado")), ) tipo_evento = models.ForeignKey( @@ -48,6 +52,18 @@ class Evento(models.Model): descricao = models.TextField(_("Descrição do evento")) virtual = models.BooleanField(_("Virtual"), default=False) solicitante = models.CharField(_("Solicitante"), max_length=100) + num_processo = models.CharField( + _(u'número do processo SIGAD'), + max_length=20, + blank=True, + help_text=_(u'Formato:XXXXX.XXXXXX/XXXX-XX') + ) + data_pedido = models.DateField( + _(u"Data do pedido"), + null=True, + blank=True, + help_text=_(u"Data em que o pedido do Gabinete chegou à COPERI") + ) data_inicio = models.DateTimeField( _("Data/hora do Início"), null=True, @@ -74,6 +90,7 @@ class Evento(models.Model): on_delete=models.PROTECT ) local = models.TextField(_("Local do evento"), blank=True) + observacao = models.TextField(_(u"Observações e anotações"), blank=True) publico_alvo = models.TextField(_("Público alvo"), blank=True) total_participantes = models.PositiveIntegerField( _("Total de participantes"), @@ -96,6 +113,20 @@ class Evento(models.Model): f"de {self.data_inicio} a {self.data_termino}" ) + def get_sigad_url(self): + m = re.match('(?P00100|00200)\.(?P\d{6})/(?P' + '\d{4})-\d{2}', self.num_processo) + if m: + return ('{processo}').format( + processo=self.num_processo,**m.groupdict() + ) + return self.num_processo + def save(self, *args, **kwargs): if self.status != 'C': self.data_cancelamento = None @@ -105,7 +136,7 @@ class Evento(models.Model): "data de início")) total = self.convite_set.aggregate(total=Sum('qtde_participantes')) total = total['total'] - if (total is not None) or (total > 0): + if total and total > 0: self.total_participantes = total return super(Evento, self).save(*args, **kwargs) diff --git a/sigi/apps/eventos/templates/admin/eventos/evento/change_list.html b/sigi/apps/eventos/templates/admin/eventos/evento/change_list.html deleted file mode 100644 index 83ebd55..0000000 --- a/sigi/apps/eventos/templates/admin/eventos/evento/change_list.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "change_list_with_cart.html" %} \ No newline at end of file diff --git a/sigi/apps/utils/filters.py b/sigi/apps/utils/filters.py index 1b1034b..777ce3d 100644 --- a/sigi/apps/utils/filters.py +++ b/sigi/apps/utils/filters.py @@ -1,11 +1,15 @@ import string from math import log10 from django import forms +from django.db.models import Q from django.contrib import admin from django.contrib.admin.options import IncorrectLookupParameters from django.utils.translation import ngettext, gettext as _ from django.core.exceptions import ValidationError +class NotEmptyableField(Exception): + pass + class AlphabeticFilter(admin.SimpleListFilter): title = '' @@ -20,6 +24,70 @@ class AlphabeticFilter(admin.SimpleListFilter): (self.parameter_name + '__istartswith', self.value()) ) +class EmptyFilter(admin.FieldListFilter): + EMPTY_STRING = _("Em branco") + NOT_EMPTY_STRING = _("Preenchido") + + 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}__empty' + + if (not field.null) and (not field.blank): + raise NotEmptyableField( + f"Field {field.name} cannot be empty nor null" + ) + + super().__init__(field, request, params, model, model_admin, field_path) + + def lookups(self): + return ( + ("1", self.EMPTY_STRING), + ("0", self.NOT_EMPTY_STRING), + ) + + def value(self): + return self.used_parameters.get(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 value, display in self.lookups(): + yield { + 'selected': self.value() == value, + 'query_string': changelist.get_query_string( + {self.parameter_name: value}), + 'display': display, + } + + def expected_parameters(self): + return [self.parameter_name,] + + def queryset(self, request, queryset): + val = self.value() + + if val is None: + return queryset + + val = bool(int(val)) + + filter = Q() + + if self.field.null: + filter = filter | Q(**{f"{self.field_path}__isnull": val}) + if self.field.blank: + if val: + filter = filter | Q(**{f"{self.field_path}__exact": ""}) + else: + filter = filter | ~Q(**{f"{self.field_path}__exact": ""}) + + return queryset.filter(filter) + class RangeFilter(admin.FieldListFilter): num_faixas = 4 parameter_name = None diff --git a/sigi/apps/utils/mixins.py b/sigi/apps/utils/mixins.py index bb60de1..7f9f19b 100644 --- a/sigi/apps/utils/mixins.py +++ b/sigi/apps/utils/mixins.py @@ -13,10 +13,17 @@ from django.urls import path from django.utils.translation import gettext as _, ngettext from import_export import resources from import_export.admin import ExportMixin +from import_export.fields import Field from import_export.forms import ExportForm from import_export.signals import post_export from sigi.apps.utils import field_label +class ValueField(Field): + def get_value(self, obj): + if self.attribute is None: + return None + return obj[self.attribute] + class ExportFormFields(ExportForm): def __init__(self, formats, field_list, *args, **kwargs): super().__init__(formats, *args, **kwargs) @@ -51,6 +58,13 @@ class LabeledResourse(resources.ModelResource): self.selected_fields = selected_fields return super().export(queryset, *args, **kwargs) +class ValueLabeledResource(LabeledResourse): + DEFAULT_RESOURCE_FIELD = ValueField + + def export(self, queryset=None, selected_fields=None, *args, **kwargs): + queryset = queryset.values(*selected_fields) + return super().export(queryset, selected_fields, *args, **kwargs) + class CartExportMixin(ExportMixin): to_encoding = 'utf-8' change_list_template = 'admin/cart/change_list_cart_export.html'