mirror of https://github.com/interlegis/sapl.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
7.2 KiB
199 lines
7.2 KiB
"""
|
|
Management command: notificar_pendentes_assinatura
|
|
|
|
Envia e-mail diario a cada autor que possui materias com assinatura digital
|
|
pendente (texto_original preenchido, pdf_assinado vazio).
|
|
|
|
Uso:
|
|
python manage.py notificar_pendentes_assinatura
|
|
|
|
Agendar (crontab) -- exemplo para rodar todo dia as 8h:
|
|
0 8 * * * cd /app && python manage.py notificar_pendentes_assinatura >> /var/log/sapl_notif_assinatura.log 2>&1
|
|
|
|
Opcoes:
|
|
--dry-run Lista os destinatarios e contagens sem enviar e-mails.
|
|
--max-materias Numero maximo de materias listadas por e-mail (padrao: 20).
|
|
"""
|
|
import logging
|
|
|
|
from django.core.management.base import BaseCommand
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
DEFAULT_MAX_MATERIAS = 20
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = 'Envia e-mail diario aos autores com materias pendentes de assinatura digital'
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument(
|
|
'--dry-run',
|
|
action='store_true',
|
|
default=False,
|
|
help='Apenas lista os destinatarios sem enviar e-mails.',
|
|
)
|
|
parser.add_argument(
|
|
'--max-materias',
|
|
type=int,
|
|
default=DEFAULT_MAX_MATERIAS,
|
|
help='Maximo de materias listadas por e-mail (padrao: 20).',
|
|
)
|
|
|
|
def handle(self, *args, **options):
|
|
# Imports aqui dentro para evitar problemas no bootstrap do Django
|
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
|
from django.db.models import Q
|
|
from django.template import loader
|
|
from django.urls import reverse
|
|
|
|
from sapl.base.models import CasaLegislativa, OperadorAutor
|
|
from sapl.materia.models import MateriaLegislativa
|
|
from sapl.settings import EMAIL_SEND_USER
|
|
from sapl.utils import mail_service_configured
|
|
|
|
dry_run = options['dry_run']
|
|
max_mat = options['max_materias']
|
|
|
|
if not dry_run and not mail_service_configured():
|
|
self.stderr.write(self.style.ERROR(
|
|
'Servico de e-mail nao configurado. '
|
|
'Verifique EMAIL_HOST no arquivo .env.'
|
|
))
|
|
return
|
|
|
|
casa = CasaLegislativa.objects.first()
|
|
if not casa:
|
|
self.stderr.write(self.style.ERROR('Casa Legislativa nao configurada.'))
|
|
return
|
|
|
|
casa_nome = '{} de {} - {}'.format(casa.nome, casa.municipio, casa.uf)
|
|
base_url = 'https://{}'.format(casa.endereco_web) if getattr(casa, 'endereco_web', None) else ''
|
|
|
|
# Base queryset: materias pendentes de assinatura
|
|
qs_pendentes = MateriaLegislativa.objects.filter(
|
|
texto_original__isnull=False,
|
|
).exclude(
|
|
texto_original=''
|
|
).filter(
|
|
Q(pdf_assinado__isnull=True) | Q(pdf_assinado='')
|
|
).select_related('tipo').order_by('-data_apresentacao', '-id')
|
|
|
|
# Apenas OperadorAutores com e-mail cadastrado
|
|
operadores = (
|
|
OperadorAutor.objects
|
|
.select_related('autor', 'user')
|
|
.filter(user__email__gt='')
|
|
)
|
|
|
|
if not operadores.exists():
|
|
self.stdout.write(self.style.WARNING(
|
|
'Nenhum OperadorAutor com e-mail encontrado. Nada a enviar.'
|
|
))
|
|
return
|
|
|
|
url_pesquisa_base = reverse('sapl.materia:pesquisar_materia')
|
|
|
|
enviados = 0
|
|
sem_pendencias = 0
|
|
|
|
connection = None if dry_run else get_connection()
|
|
if connection:
|
|
connection.open()
|
|
|
|
try:
|
|
for op in operadores:
|
|
autor = op.autor
|
|
user = op.user
|
|
email = (user.email or '').strip()
|
|
|
|
if not email:
|
|
continue
|
|
|
|
# Materias pendentes deste autor
|
|
materias_qs = qs_pendentes.filter(autoria__autor=autor).distinct()
|
|
total = materias_qs.count()
|
|
|
|
if total == 0:
|
|
sem_pendencias += 1
|
|
continue
|
|
|
|
materias_listadas = list(materias_qs[:max_mat])
|
|
total_omitidas = max(0, total - max_mat)
|
|
|
|
url_pesquisa = (
|
|
'{}?autoria__autor={}&status_assinatura=pendente'.format(
|
|
url_pesquisa_base, autor.pk
|
|
)
|
|
)
|
|
|
|
context = {
|
|
'casa_legislativa': casa_nome,
|
|
'nome_autor': autor.nome,
|
|
'total': total,
|
|
'materias': materias_listadas,
|
|
'total_omitidas': total_omitidas,
|
|
'base_url': base_url,
|
|
'url_pesquisa': url_pesquisa,
|
|
}
|
|
|
|
subject = '[SGVP] {} materia(s) aguardando sua assinatura digital'.format(total)
|
|
|
|
if dry_run:
|
|
self.stdout.write(self.style.SUCCESS(
|
|
'[DRY-RUN] -> {} ({}): {} pendente(s)'.format(email, autor.nome, total)
|
|
))
|
|
for m in materias_listadas:
|
|
self.stdout.write(
|
|
' * {} {}/{} -- {}'.format(
|
|
m.tipo.sigla, m.numero, m.ano, m.ementa[:60]
|
|
)
|
|
)
|
|
if total_omitidas:
|
|
self.stdout.write(' ... e mais {} outra(s).'.format(total_omitidas))
|
|
continue
|
|
|
|
# Renderiza templates
|
|
txt_body = loader.get_template('email/pendentes_assinatura.txt').render(context)
|
|
html_body = loader.get_template('email/pendentes_assinatura.html').render(context)
|
|
|
|
try:
|
|
msg = EmailMultiAlternatives(
|
|
subject=subject,
|
|
body=txt_body,
|
|
from_email=EMAIL_SEND_USER,
|
|
to=[email],
|
|
connection=connection,
|
|
)
|
|
msg.attach_alternative(html_body, 'text/html')
|
|
msg.send()
|
|
enviados += 1
|
|
logger.info(
|
|
'[notificar_pendentes_assinatura] E-mail enviado para '
|
|
'{} ({}) -- {} pendente(s).'.format(email, autor.nome, total)
|
|
)
|
|
self.stdout.write(self.style.SUCCESS(
|
|
'E-mail enviado para {} ({}) -- {} pendente(s).'.format(
|
|
email, autor.nome, total
|
|
)
|
|
))
|
|
except Exception as exc:
|
|
logger.error(
|
|
'[notificar_pendentes_assinatura] Falha ao enviar para '
|
|
'{}: {}'.format(email, exc)
|
|
)
|
|
self.stderr.write(self.style.ERROR(
|
|
'Falha ao enviar para {}: {}'.format(email, exc)
|
|
))
|
|
|
|
finally:
|
|
if connection:
|
|
connection.close()
|
|
|
|
if not dry_run:
|
|
self.stdout.write(self.style.SUCCESS(
|
|
'\nConcluido: {} e-mail(s) enviado(s). '
|
|
'{} autor(es) sem pendencias (nao notificados).'.format(
|
|
enviados, sem_pendencias
|
|
)
|
|
))
|
|
|