From c88e2a55baa9a5fa242be9f7ddfbac121daa85a6 Mon Sep 17 00:00:00 2001 From: Claudio Morale Date: Wed, 15 Aug 2012 18:42:29 +0000 Subject: [PATCH] =?UTF-8?q?Nova=20APP=20de=20servicos=20para=20atender=20a?= =?UTF-8?q?=20demanda=20da=20SEIT,=20que=20deseja=20controlar=20os=20servi?= =?UTF-8?q?=C3=A7os=20de=20hospedagem=20que=20o=20Interlegis=20presta=20?= =?UTF-8?q?=C3=A0s=20Casas=20Legislativas.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pydevproject | 10 +- sigi/apps/servicos/admin.py | 168 ++++++++++++++--- sigi/apps/servicos/fixtures/initial_data.json | 1 + sigi/apps/servicos/models.py | 169 +++++++++++------- sigi/sites.py | 6 +- .../servicos/casaatendida/change_form.html | 42 +++++ .../servicos/casaatendida/change_list.html | 26 +++ sigi/urls.py | 3 +- 8 files changed, 328 insertions(+), 97 deletions(-) create mode 100644 sigi/apps/servicos/fixtures/initial_data.json create mode 100644 sigi/templates/admin/servicos/casaatendida/change_form.html create mode 100644 sigi/templates/admin/servicos/casaatendida/change_list.html diff --git a/.pydevproject b/.pydevproject index f3f5391..a2ba8c5 100644 --- a/.pydevproject +++ b/.pydevproject @@ -1,5 +1,6 @@ - + + @@ -34,12 +35,13 @@ - /sigi - +/sigi +/sigi/sigi + - \ No newline at end of file + diff --git a/sigi/apps/servicos/admin.py b/sigi/apps/servicos/admin.py index b333ada..181f632 100644 --- a/sigi/apps/servicos/admin.py +++ b/sigi/apps/servicos/admin.py @@ -1,30 +1,148 @@ # -*- coding: utf-8 -*- - from django.contrib import admin -from django.contrib.contenttypes import generic -from sigi.apps.contatos.models import Contato -from sigi.apps.servicos.models import Servico -from sigi.apps.servicos.models import DominioLeg - -class ContatosInline(generic.GenericTabularInline): - model = Contato - extra = 2 - raw_id_fields = ('municipio',) - verbose_name = 'colaborador Interlegis' - verbose_name_plural = 'colaboradores Interlegis' +from sigi.apps.servicos.models import Servico, LogServico, CasaAtendida, TipoServico +#from sigi.apps.casas.models import Funcionario +from sigi.apps.casas.admin import FuncionariosInline +from django.http import Http404, HttpResponseRedirect +from django.forms.models import ModelForm +from django.utils.encoding import force_unicode +from django.utils.translation import ugettext as _ +from django.core.urlresolvers import reverse +from apps.casas.models import CasaLegislativa + +#---------------- inlines --------------------- +class LogServicoInline(admin.StackedInline): + model = LogServico + Fieldset = ((None, {'fields': (('data', 'descricao'), 'log')})) + extra = 1 +# --------------- forms ----------------------- +class ServicoFormAdmin(ModelForm): + class Meta: + model = Servico + + def __init__(self, *args, **kwargs): + super(ServicoFormAdmin, self).__init__(*args, **kwargs) + + self.fields['contato_tecnico'].choices = () + self.fields['contato_administrativo'].choices = () + + if self.instance.casa_legislativa_id: + id_casa = self.instance.casa_legislativa_id + elif kwargs.has_key('initial') and kwargs['initial'].has_key('id_casa'): + id_casa = kwargs['initial']['id_casa'] + self.instance.casa_legislativa_id = id_casa + else: + id_casa = None + + if id_casa: + casa = CasaAtendida.objects.get(pk=id_casa) + contatos = [(f.id, unicode(f)) for f in casa.funcionario_set.all()] + self.fields['contato_tecnico'].choices = contatos + self.fields['contato_administrativo'].choices = contatos + +#---------------- admins ---------------------- +class TipoServicoAdmin(admin.ModelAdmin): + list_display = ('id', 'sigla', 'nome', ) + ordering = ['id'] + class ServicoAdmin(admin.ModelAdmin): - date_hierarchy = 'data_inicio' - inlines = (ContatosInline,) - list_display = ('id', 'titulo', 'tipo', 'convenio', 'situacao') - list_filter = ('tipo','situacao', 'avaliacao') - raw_id_fields = ('convenio',) - search_fields = ('titulo', 'tipo', 'descricao') -class DominiolegAdmin(admin.ModelAdmin): - model = DominioLeg - date_hierarchy = 'data_preenchimento' - list_display = ('id', 'dominio', 'contato_administrativo', 'contato_tecnico', 'data_preenchimento', 'data_recebimento', 'data_atendimento',) - search_fields = ('dominio',) + form = ServicoFormAdmin + list_display = ('casa_legislativa', 'tipo_servico', 'hospedagem_interlegis', 'data_ativacao', 'data_desativacao',) + fieldsets = (( None, { + 'fields': ('casa_legislativa', 'data_ativacao',) + }), + ( 'Serviço', { + 'fields': ('tipo_servico', ('url', 'hospedagem_interlegis'), ('nome_servidor', 'porta_servico', 'senha_inicial'),) + }), + ( 'Contatos', { + 'fields': ('contato_tecnico', 'contato_administrativo',) + }), + ( 'Alterações', { + 'fields': ('data_alteracao', 'data_desativacao', 'motivo_desativacao',) + })) + readonly_fields = ('casa_legislativa', 'data_ativacao', 'data_alteracao') + + inlines = (LogServicoInline,) + + def add_view(self, request, form_url='', extra_context=None): + id_casa = request.GET.get('id_casa', None) + + if not id_casa: + raise Http404 + + return super(ServicoAdmin, self).add_view(request, form_url, extra_context=extra_context) + + def response_add(self, request, obj): + opts = obj._meta + msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} + + if request.POST.has_key("_addanother"): + self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) + return HttpResponseRedirect(request.path + '?id_casa=%s' % (obj.casa_legislativa.id,)) + elif request.POST.has_key("_save"): + self.message_user(request, msg) + return HttpResponseRedirect(reverse('admin:servicos_casaatendida_change', args=[obj.casa_legislativa.id])) + + return super(ServicoAdmin, self).response_add(request, obj) + + def response_change(self, request, obj): + opts = obj._meta + msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} + + if request.POST.has_key("_addanother"): + self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) + return HttpResponseRedirect("../add/?id_casa=%s" % (obj.casa_legislativa.id,)) + elif request.POST.has_key("_save"): + self.message_user(request, msg) + return HttpResponseRedirect(reverse('admin:servicos_casaatendida_change', args=[obj.casa_legislativa.id])) + + return super(ServicoAdmin, self).response_change(request, obj) + + def save_form(self, request, form, change): + obj = super( ServicoAdmin, self).save_form(request, form, change) + + if not change: + id_casa = request.GET.get('id_casa', None) + + if not id_casa: + raise Http404 + + obj.casa_legislativa = CasaAtendida.objects.get(pk=id_casa) + + return obj + +class ContatosInline(FuncionariosInline): + can_delete = False # Equipe do SEIT não pode excluir pessoas de contato + +class CasaAtendidaAdmin(admin.ModelAdmin): + actions = None + list_display = ('codigo_interlegis', 'nome', 'servicos',) + ordering = ['nome'] + fieldsets = ( + ('Casa legislativa', { + 'fields': (('codigo_interlegis', 'nome'), ('logradouro', 'bairro', 'municipio', 'cep'), ('email', 'pagina_web')) + }) + ,) + readonly_fields = ('nome', 'logradouro', 'bairro', 'municipio', 'cep') + inlines = (ContatosInline,) + list_filter = ('tipo', 'municipio') + search_fields = ('search_text','cnpj', 'bairro', 'logradouro', + 'cep', 'municipio__nome', 'municipio__uf__nome', + 'municipio__codigo_ibge', 'pagina_web', 'observacoes') + + def change_view(self, request, object_id, extra_context=None): + # Se a Casa ainda não é atendida, gerar o código interlegis para ela + # Assim ela passa a ser uma casa atendida + casa = CasaLegislativa.objects.get(id=object_id) + + if casa.codigo_interlegis == '': + casa.gerarCodigoInterlegis() + + return super(CasaAtendidaAdmin, self).change_view(request, object_id, extra_context=extra_context) + + def has_add_permission(self, request): + return False # Nunca é permitido inserir uma nova Casa Legislativa por aqui -admin.site.register(DominioLeg, DominiolegAdmin) -admin.site.register(Servico, ServicoAdmin) \ No newline at end of file + def has_delete_permission(self, request, obj=None): + return False # Nunca deletar casas por aqui \ No newline at end of file diff --git a/sigi/apps/servicos/fixtures/initial_data.json b/sigi/apps/servicos/fixtures/initial_data.json new file mode 100644 index 0000000..4671d13 --- /dev/null +++ b/sigi/apps/servicos/fixtures/initial_data.json @@ -0,0 +1 @@ +[{"pk": 1, "model": "servicos.tiposervico", "fields": {"template_email_altera": "Seu Portal Modelo foi alterado com sucesso. O endere\u00e7o de acesso \u00e9 {url} e a senha \u00e9 {senha}. Altere sua senha no primeiro acesso.", "sigla": "PM", "template_email_ativa": "Seu Portal Modelo foi ativado com sucesso. O endere\u00e7o de acesso \u00e9 {url} e a senha inicial \u00e9 {senha}. Altere sua senha no primeiro acesso.", "template_email_desativa": "Seu Portal Modelo foi desativado com sucesso. O endere\u00e7o de acesso era {url}.\r\nO motivo do cancelamento foi: {motivo}", "nome": "Portal Modelo"}}, {"pk": 2, "model": "servicos.tiposervico", "fields": {"template_email_altera": "Seu SAPL foi alterado com sucesso. O endere\u00e7o de acesso \u00e9 {url} e a senha \u00e9 {senha}. Altere sua senha no primeiro acesso.", "sigla": "SAPL", "template_email_ativa": "Seu SAPL foi ativado com sucesso. O endere\u00e7o de acesso \u00e9 {url} e a senha inicial \u00e9 {senha}. Altere sua senha no primeiro acesso.", "template_email_desativa": "Seu SAPL foi desativado com sucesso. O endere\u00e7o de acesso era {url}.\r\nO motivo do cancelamento foi: {motivo}", "nome": "Hospedagem SAPL"}}, {"pk": 3, "model": "servicos.tiposervico", "fields": {"template_email_altera": "Seu SAAP foi alterado com sucesso. O endere\u00e7o de acesso \u00e9 {url} e a senha \u00e9 {senha}. Altere sua senha no primeiro acesso.", "sigla": "SAAP", "template_email_ativa": "Seu SAAP foi ativado com sucesso. O endere\u00e7o de acesso \u00e9 {url} e a senha inicial \u00e9 {senha}. Altere sua senha no primeiro acesso.", "template_email_desativa": "Seu SAAP foi desativado com sucesso. O endere\u00e7o de acesso era {url}.\r\nO motivo do cancelamento foi: {motivo}", "nome": "Hospedagem SAAP"}}] \ No newline at end of file diff --git a/sigi/apps/servicos/models.py b/sigi/apps/servicos/models.py index 706bdb1..4172283 100644 --- a/sigi/apps/servicos/models.py +++ b/sigi/apps/servicos/models.py @@ -1,78 +1,117 @@ # -*- coding: utf-8 -*- from django.db import models -from django.contrib.contenttypes import generic -from sigi.apps.casas.models import CasaLegislativa +from sigi.apps.casas.models import CasaLegislativa, Funcionario from datetime import date +from django.core.mail import send_mail +from sigi.settings import DEFAULT_FROM_EMAIL -class Servico(models.Model): - SITUACAO_CHOICES = ( - ('P', 'Pendente'), - ('A', 'Em andamento'), - ('E', 'Executado'), - ('D', 'Demanda'), - ('C', 'Cancelado'), - ) - AVALIACAO_CHOICES = ( - (4, 'Ótimo'), - (3, 'Bom'), - (2, 'Regular'), - (1, 'Ruim'), - ) - titulo = models.CharField('título', max_length=60) - tipo = models.CharField(max_length=30) - descricao = models.TextField(u'descrição') - convenio = models.ForeignKey('convenios.Convenio', verbose_name='Convênio') - colaboradores = generic.GenericRelation('contatos.Contato') - data_inicio = models.DateField( - u'início', - blank=True, - null=True, - help_text = 'Início da realização do serviço.', - ) - data_fim = models.DateField( - 'fim', - blank=True, - null=True, - help_text = 'Fim da realização do serviço.', - ) - situacao = models.CharField( - u'situação', - max_length=1, - choices=SITUACAO_CHOICES - ) - avaliacao = models.PositiveSmallIntegerField( - u'avaliação', - choices=AVALIACAO_CHOICES, - blank=True, - null=True, - help_text='Avaliação que o serviço obteve, quando aplicável.' - ) +class TipoServico(models.Model): + email_help = '''Use:
+ {url} para incluir a URL do serviço,
+ {senha} para incluir a senha inicial do serviço''' + nome = models.CharField('Nome', max_length=60) + sigla = models.CharField('Sigla', max_length='12') + template_email_ativa = models.TextField('Template de email de ativação', help_text = email_help, blank=True) + template_email_altera = models.TextField('Template de email de alteração', help_text = email_help, blank=True) + template_email_desativa = models.TextField('Template de email de desativação', help_text = email_help + '
{motivo} para incluir o motivo da desativação do serviço', blank=True) class Meta: - verbose_name = 'serviço' - verbose_name_plural = 'serviços' - + verbose_name = 'Tipo de serviço' + verbose_name_plural = 'Tipos de serviço' + def __unicode__(self): - return str(self.titulo) + return self.nome; -class DominioLeg(models.Model): - casa_legislativa = models.OneToOneField(CasaLegislativa) - dominio = models.URLField('Domínio', verify_exists=False) - contato_administrativo = models.CharField('Contato administrativo', max_length=60) - telefone_administrativo = models.CharField('Telefone administrativo', max_length=10, help_text='Somente números: ddaaaannnn.') - email_administrativo = models.EmailField('e-mail') - contato_tecnico = models.CharField('Contato técnico', max_length=60) - telefone_tecnico = models.CharField('Telefone administrativo', max_length=10, help_text='Somente números: ddaaaannnn.') - email_tecnico = models.EmailField('e-mail') - data_preenchimento = models.DateField('Data de preenchimento', default=date.today) - data_recebimento = models.DateField('Data de recebimento', null=True, blank=True) - data_atendimento = models.DateField('Data de atendimento', null=True, blank=True) +class Servico(models.Model): + casa_legislativa = models.ForeignKey(CasaLegislativa, verbose_name='Casa legislativa') + tipo_servico = models.ForeignKey(TipoServico, verbose_name='Tipo de serviço') + contato_tecnico = models.ForeignKey(Funcionario, verbose_name='Contato técnico', related_name='contato_tecnico') + contato_administrativo = models.ForeignKey(Funcionario, verbose_name='Contato administrativo', related_name='contato_administrativo') + url = models.URLField('URL do serviço', verify_exists=False, blank=True) + hospedagem_interlegis = models.BooleanField('Hospedagem no Interlegis?') + nome_servidor = models.CharField('Hospedado em', max_length=60, blank=True, help_text='Se hospedado no Interlegis, informe o nome do servidor.
Senão, informe o nome do provedor de serviços.') + porta_servico = models.PositiveSmallIntegerField('Porta de serviço (instância)', blank=True, null=True) + senha_inicial = models.CharField('Senha inicial', max_length=33, blank=True) + data_ativacao = models.DateField('Data de ativação', default=date.today) + data_alteracao = models.DateField('Data da última alteração', blank=True, null=True, auto_now=True) + data_desativacao = models.DateField('Data de desativação', blank=True, null=True) + motivo_desativacao = models.TextField('Motivo da desativação', blank=True) - class Meta: - verbose_name = 'Registro de domínio .leg.br' - verbose_name_plural = 'Registros de domínios .leg.br' + def __unicode__(self): + return "%s (%s)" % (self.tipo_servico.nome, 'ativo' if self.data_desativacao is None else 'Desativado') + + def save(self, *args, **kwargs): + # Reter o objeto original para verificar mudanças + + if self.id is not None: + original = Servico.objects.get(id=self.id) + + if self.id is None: + # Novo serviço, email de ativação + subject = 'INTERLEGIS - Ativação de serviço %s' % (self.tipo_servico.nome,) + body = self.tipo_servico.template_email_ativa + elif self.data_desativacao is not None and original.data_desativacao is None: + # Serviço foi desativado. Email de desativação + subject = 'INTERLEGIS - Desativação de serviço %s' % (self.tipo_servico.nome,) + body = self.tipo_servico.template_email_desativa + elif (self.tipo_servico != original.tipo_servico or + self.contato_tecnico != original.contato_tecnico or + self.url != original.url or + self.nome_servidor != original.nome_servidor or + self.senha_inicial != original.senha_inicial): + # Serviço foi alterado + subject = 'INTERLEGIS - Alteração de serviço %s' % (self.tipo_servico.nome,) + body = self.tipo_servico.template_email_altera + else: + # Salvar o Servico + super(Servico, self).save(*args, **kwargs) + return # sem enviar email + + # Prepara e envia o email + body = body.replace('{url}', self.url) \ + .replace('{senha}', self.senha_inicial) \ + .replace('{motivo}', self.motivo_desativacao) + +# send_mail(subject, body, DEFAULT_FROM_EMAIL, \ +# (self.contato_tecnico.email,), fail_silently=False) + # Salvar o Servico + super(Servico, self).save(*args, **kwargs) + + return + +class LogServico(models.Model): + servico = models.ForeignKey(Servico, verbose_name='Serviço') + descricao = models.CharField('Breve descrição da ação', max_length=60) + data = models.DateField('Data da ação', default=date.today) + log = models.TextField('Log da ação') + def __unicode__(self): - return str(self.dominio) + return "%s (%s)" % (self.descricao, self.data) + class Meta: + verbose_name = 'Log do serviço' + verbose_name_plural = 'Logs do serviço' + +class CasaAtendidaManager(models.Manager): + def get_query_set(self): + qs = super(CasaAtendidaManager, self).get_query_set() + qs = qs.exclude(codigo_interlegis='') + return qs + +class CasaAtendida(CasaLegislativa): + class Meta: + proxy = True + verbose_name_plural = 'Casas atendidas' + + objects = CasaAtendidaManager() + @property + def servicos(self): + qs = Servico.objects.filter(casa_legislativa=self.id) + result = [] + + for servico in qs: + result.append(unicode(servico)) + + return ", ".join(result) \ No newline at end of file diff --git a/sigi/sites.py b/sigi/sites.py index c5041bb..b3ffeac 100644 --- a/sigi/sites.py +++ b/sigi/sites.py @@ -14,7 +14,8 @@ from sigi.apps.inventario.admin import (Fornecedor, FornecedorAdmin, Fabricante, EquipamentoAdmin, TipoEquipamento, TipoEquipamentoAdmin, ModeloEquipamento, ModeloEquipamentoAdmin, Bem, BemAdmin) -from sigi.apps.servicos.admin import Servico, ServicoAdmin, DominioLeg, DominiolegAdmin +from sigi.apps.servicos.admin import (TipoServico, TipoServicoAdmin, CasaAtendida, + CasaAtendidaAdmin, Servico, ServicoAdmin) from sigi.apps.mesas.admin import (Legislatura, LegislaturaAdmin, Coligacao, ColigacaoAdmin, ComposicaoColigacao, ComposicaoColigacaoAdmin, SessaoLegislativa, @@ -75,8 +76,9 @@ default.register(Equipamento, EquipamentoAdmin) default.register(Bem, BemAdmin) # sigi.apps.servicos +default.register(TipoServico, TipoServicoAdmin) default.register(Servico, ServicoAdmin) -default.register(DominioLeg, DominiolegAdmin) +default.register(CasaAtendida, CasaAtendidaAdmin) # sigi.apps.mesas default.register(Legislatura, LegislaturaAdmin) diff --git a/sigi/templates/admin/servicos/casaatendida/change_form.html b/sigi/templates/admin/servicos/casaatendida/change_form.html new file mode 100644 index 0000000..7b55bf5 --- /dev/null +++ b/sigi/templates/admin/servicos/casaatendida/change_form.html @@ -0,0 +1,42 @@ +{% extends "admin/change_form.html" %} +{% load i18n admin_modify adminmedia %} + +{% block after_related_objects %} +{{ block.super }} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/sigi/templates/admin/servicos/casaatendida/change_list.html b/sigi/templates/admin/servicos/casaatendida/change_list.html new file mode 100644 index 0000000..49c451f --- /dev/null +++ b/sigi/templates/admin/servicos/casaatendida/change_list.html @@ -0,0 +1,26 @@ +{% extends "admin/change_list.html" %} +{% load adminmedia admin_list i18n %} + +{% block extrahead %} +{{ block.super }} + + +{% endblock %} + +{% block object-tools %} + + +{% endblock %} diff --git a/sigi/urls.py b/sigi/urls.py index ec38f1e..7645913 100644 --- a/sigi/urls.py +++ b/sigi/urls.py @@ -87,7 +87,8 @@ urlpatterns = patterns( (r'^sigi/api/diagnosticos/$', 'sigi.apps.diagnosticos.views.grafico_api'), # automatic interface based on admin - (r'^sigi/(.*)', sites.default.root), + #(r'^sigi/(.*)', sites.default.root), + (r'^sigi/', include(sites.default.urls)), ) if settings.DEBUG: