Browse Source

Replicando alterações emergenciais do SIGI legado

pull/159/head
Sesostris Vieira 3 years ago
parent
commit
88c3365f0e
  1. 18
      sigi/apps/convenios/migrations/0020_gescon_orgaos_gestores.py
  2. 179
      sigi/apps/convenios/models.py
  3. 63
      sigi/apps/eventos/admin.py
  4. 8
      sigi/apps/eventos/forms.py
  5. 33
      sigi/apps/eventos/migrations/0018_evento_data_pedido_evento_num_processo_and_more.py
  6. 18
      sigi/apps/eventos/migrations/0019_alter_evento_status.py
  7. 35
      sigi/apps/eventos/models.py
  8. 1
      sigi/apps/eventos/templates/admin/eventos/evento/change_list.html
  9. 68
      sigi/apps/utils/filters.py
  10. 14
      sigi/apps/utils/mixins.py

18
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<ul><li>Informe um sigla por linha.</li><li>Ocorrendo qualquer uma das siglas, o contrato será importado.</li></ul>', verbose_name='Órgãos gestores'),
),
]

179
sigi/apps/convenios/models.py

@ -381,6 +381,15 @@ class Gescon(models.Model):
"<li>Ocorrendo qualquer uma das palavras, o contrato será "
"importado.</li></ul>")
)
orgaos_gestores = models.TextField(
_("Órgãos gestores"),
default="SCCO",
help_text=_("Siglas de órgãos gestores que devem aparecer no campo"
"ORGAOSGESTORESTITULARES"
"<ul><li>Informe um sigla por linha.</li>"
"<li>Ocorrendo qualquer uma das siglas, o contrato será "
"importado.</li></ul>")
)
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,8 +548,11 @@ class Gescon(models.Model):
orgao = None
if cnpj is not None:
if cnpj in lista_cnpj:
orgao = lista_cnpj[cnpj]
else:
try:
orgao = Orgao.objects.get(cnpj=cnpj)
orgao = Orgao.objects.get(cnpj=cnpj_masked)
except (
Orgao.DoesNotExist,
Orgao.MultipleObjectsReturned) as e:
@ -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,18 +639,11 @@ 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 += _(
@ -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()

63
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

8
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()

33
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:<em>XXXXX.XXXXXX/XXXX-XX</em>', 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'),
),
]

18
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'),
),
]

35
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:<em>XXXXX.XXXXXX/XXXX-XX</em>')
)
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('(?P<orgao>00100|00200)\.(?P<sequencial>\d{6})/(?P<ano>'
'\d{4})-\d{2}', self.num_processo)
if m:
return ('<a href="https://intra.senado.leg.br/'
'sigad/novo/protocolo/impressao.asp?area=processo'
'&txt_numero_orgao={orgao}'
'&txt_numero_sequencial={sequencial}'
'&txt_numero_ano={ano}"'
' target="_blank">{processo}</a>').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)

1
sigi/apps/eventos/templates/admin/eventos/evento/change_list.html

@ -1 +0,0 @@
{% extends "change_list_with_cart.html" %}

68
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

14
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'

Loading…
Cancel
Save