Browse Source

Realease com funcionalidades de diagnosticos e recursos humanos. Merge do branch diagnosticos.

stable/1.0
Felipe Vieira 13 years ago
parent
commit
c7d4e35efb
  1. 14
      media/css/admin/diagnosticos/diagnostico/change_form.css
  2. 27
      media/css/diagnosticos/diagnosticos_categoria_model_form.css
  3. 26
      media/css/diagnosticos/diagnosticos_categorias.css
  4. 13
      media/css/diagnosticos/diagnosticos_categorias_form.css
  5. BIN
      media/images/loader.gif
  6. BIN
      media/images/mobile/logointerlegis_mobile.png
  7. 97
      media/js/diagnosticos/diagnosticos_categorias_form.js
  8. 12
      requirements.txt
  9. 79
      setup.sh
  10. 23
      sigi/admin/filterspecs.py
  11. 32
      sigi/apps/casas/admin.py
  12. 6
      sigi/apps/casas/forms.py
  13. 56
      sigi/apps/casas/models.py
  14. 8
      sigi/apps/contatos/admin.py
  15. 24
      sigi/apps/contatos/models.py
  16. 74
      sigi/apps/diagnosticos/admin.py
  17. 23
      sigi/apps/diagnosticos/decorators.py
  18. 1
      sigi/apps/diagnosticos/fixtures/initial_data.json
  19. 145
      sigi/apps/diagnosticos/forms.py
  20. 153
      sigi/apps/diagnosticos/models.py
  21. 15
      sigi/apps/diagnosticos/tests.py
  22. 35
      sigi/apps/diagnosticos/urls.py
  23. 187
      sigi/apps/diagnosticos/views.py
  24. 61
      sigi/apps/diagnosticos/widgets.py
  25. 2
      sigi/apps/servicos/models.py
  26. 69
      sigi/apps/servidores/admin.py
  27. 60
      sigi/apps/servidores/forms.py
  28. 0
      sigi/apps/servidores/management/__init__.py
  29. 0
      sigi/apps/servidores/management/commands/__init__.py
  30. 233
      sigi/apps/servidores/management/commands/migra.py
  31. 93
      sigi/apps/servidores/management/commands/sync_ldap.py
  32. 62
      sigi/apps/servidores/models.py
  33. 16
      sigi/apps/utils/admin_widgets.py
  34. 71
      sigi/apps/utils/decorators.py
  35. 33
      sigi/apps/utils/email.py
  36. 42
      sigi/apps/utils/validators.py
  37. 2
      sigi/fixtures/treemenus/initial_data.json
  38. 19
      sigi/migrations/20111125_casas_funcionario.sql
  39. 7
      sigi/migrations/20111125_diagnosticos_responsavel.sql
  40. 23
      sigi/settings.py
  41. 10
      sigi/sites.py
  42. 6
      sigi/templates/admin/diagnosticos/diagnostico/change_form.html
  43. 27
      sigi/templates/admin/servidores/servidor/change_form.html
  44. 54
      sigi/templates/base_mobile.html
  45. 49
      sigi/templates/diagnosticos/diagnosticos_categoria_casa_legislativa_form.html
  46. 52
      sigi/templates/diagnosticos/diagnosticos_categoria_contatos_form.html
  47. 43
      sigi/templates/diagnosticos/diagnosticos_categorias_form.html
  48. 63
      sigi/templates/diagnosticos/diagnosticos_categorias_list.html
  49. 26
      sigi/templates/diagnosticos/diagnosticos_list.html
  50. 51
      sigi/templates/diagnosticos/diagnosticos_login.html
  51. 8
      sigi/templates/diagnosticos/email_diagnostico_atualizado.txt
  52. 10
      sigi/templates/diagnosticos/email_diagnostico_publicado.txt
  53. 15
      sigi/templates/mobile/404.html
  54. 74
      sigi/urls.py

14
media/css/admin/diagnosticos/diagnostico/change_form.css

@ -0,0 +1,14 @@
form .aligned label {
float: none;
width: 100%;
}
form .aligned ul {
margin-left: 0px;
padding-left: 0px;
}
form .aligned ul li {
list-style: none;
}
form .form-row.data_visita_inicio {
float: left;
}

27
media/css/diagnosticos/diagnosticos_categoria_model_form.css

@ -0,0 +1,27 @@
div.ui-field-contain input.ui-input-text, div.ui-field-contain textarea.ui-input-text, div.ui-field-contain .ui-input-search {
width: 97%;
display: inline-block;
}
div.ui-field-contain.phone input.ui-input-text {
width: 65%;
display: inline-block;
}
div.ui-field-contain.phone .ui-select {
width: 30%;
float: left;
margin: -0.5em 0;
}
div.ui-field-contain.phone .ui-select .ui-btn-text {
text: left;
padding-left: 0.2em;
}
div.ui-field-contain.phone .ui-select .ui-btn {
margin: 0.5em 10px 0.5em 0px;
}
div.ui-field-contain.phone .ui-select .ui-btn-inner {
padding-left: 15px;
}

26
media/css/diagnosticos/diagnosticos_categorias.css

@ -0,0 +1,26 @@
/* Destacando a ultima categoria visitada */
.ui-btn-last-c {
border: 1px solid #155678;
background: #4596CE;
font-weight: bold;
cursor: pointer;
text-shadow: 0 -1px 1px #145072;
text-decoration: none;
background-image: -webkit-gradient(linear,left top,left bottom,from(#85bae4),to(#5393c5));
background-image: -webkit-linear-gradient(#85bae4,#5393c5);
background-image: -moz-linear-gradient(#85bae4,#5393c5);
background-image: -ms-linear-gradient(#85bae4,#5393c5);
background-image: -o-linear-gradient(#85bae4,#5393c5);
background-image: linear-gradient(#85bae4,#5393c5);
font-family: Helvetica,Arial,sans-serif;
}
.ui-btn-last-c a.ui-link-inherit {
color: white;
}
/* Definindo as categorias que foram lidas */
.ui-li-heading-read {
font-weight: normal !important;
}

13
media/css/diagnosticos/diagnosticos_categorias_form.css

@ -0,0 +1,13 @@
.ui-field-contain input.ui-input-text, .ui-field-contain textarea.ui-input-text, .ui-field-contain .ui-input-search {
width: 97%;
display: inline-block;
}
.ui-field-contain ul {
-webkit-padding-start: 0;
}
span.errors {
display: block;
color: red;
}

BIN
media/images/loader.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
media/images/mobile/logointerlegis_mobile.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

97
media/js/diagnosticos/diagnosticos_categorias_form.js

@ -0,0 +1,97 @@
// cntabiliza a quantidade de requests
// ajax para nao desabilitar o loader
// antes da hora
var nun_ajax = 0;
$('#page').live('pageinit', function(event){
// variaveis globais para as requisicoes ajax
$.ajaxSetup({
url: $(location).attr('href'),
cache: false,
type: 'POST',
beforeSend: function() {
nun_ajax++;
$('#working').show();
},
success: function(data) {
nun_ajax--;
if (nun_ajax == 0)
$('#working').hide();
//Retirando o span existente
$("span.errors").html("");
if (data.mensagem == "erro") {
for (var campo in data.erros) {
$("#"+ campo + " span").html(data.erros[campo].join('\n'))
}
}
},
error: function(msg) {
nun_ajax--;
if (nun_ajax == 0)
$('#working').hide();
$("#open-dialog").click();
}
});
// remove a resposta vazia da interface
$("div.ui-radio span.ui-btn-text:contains('---------')").parentsUntil("ul").hide();
// para todo input do from registra um evento
// ao modificar o campo
$("div.ui-field-contain textarea, div.ui-field-contain input, div.ui-field-contain select").change(function () {
// mostra ou esconde uma pergunta dependente
var id_to_open = []
var id_to_close = []
$('input[name=' + $(this).attr('name') + ']').each(function () {
schema = $(this);
schema_to_open = schema.attr('schema_to_open');
if (schema_to_open) {
if (schema.is(':checked'))
id_to_open.push(schema_to_open)
else
id_to_close.push(schema_to_open)
}
});
for (var i in id_to_close) {
id = id_to_close[i]
// Evita apagar uma pergunta caso ela possa
// ser exibida por outra questão
if (id_to_open.indexOf(id_to_close[i]) == -1) {
// limpa o valor para não salva-lo
// no submit do form
$("#" + id + " input").val('');
$("#" + id).slideUp();
}
}
// Exibe as perguntas que devem estar disponiveis
for (var i in id_to_open) {
id = id_to_open[i]
$("#" + id).slideDown();
}
$.ajax({
data: $('#diagnostico').serializeArray()
});
});
$('input[schema_to_open]').each(function () {
schema = $(this);
schema_to_open = $("#" + schema.attr('schema_to_open'));
schema_to_open.hide();
});
$('input[schema_to_open]:checked').each(function () {
schema = $(this);
schema_to_open = $("#" + schema.attr('schema_to_open'));
schema_to_open.show();
});
// se carregou o js sem erros mostra as perguntas
$("#waiting").hide();
$("#working").hide();
$("#form").show();
});

12
requirements.txt

@ -0,0 +1,12 @@
PIL==1.1.7
Django==1.2
django-auth-ldap==1.0.12
django-autoslug==1.4.1
django-extensions==0.7.1
django-treemenus==0.8.7
eav-django==1.3.4
html5lib==0.90
pisa==3.0.33
python-ldap==2.4.4
reportlab==2.5
wsgiref==0.1.2

79
setup.sh

@ -0,0 +1,79 @@
#!bin/bash
#############################################################
## Arquivo: setup.sh ##
## ##
## Esse arquivo foi criado para automatizar a instalação ##
## do projeto SIGI. ##
## ##
## Autor: Gilson Filho <contato@gilsondev.com> ##
## Data: 23 de Novembro de 2011 ##
## Versão: 1.0 ##
## ##
#############################################################
# Definindo o nome do arquivo que contem as informações das dependências
requirements="requirements.txt"
# Verifica se o easy_install e o pip está instalado
easy_install=`find /usr/bin/ -name easy_install`
pip=`find /usr/bin/ -name pip`
git=`find /usr/bin/ -name git`
if [ ! -f $easy_install ] || [ ! -f $pip ]; then
echo "O aplicativo pip é obrigatório. Favor instalar para continuar a configuração do SIGI."
sleep 5
exit
else
# Executando o arquivo requirements.txt
if [ -f $requirements ]; then
echo
echo "Instalando os módulos contidos no arquivo $requirements ..."
echo
sleep 2
pip install -r $requirements
# Verifica se o git está instalado
if [ ! -f $git]; then
echo
echo "O aplicativo git não está instalado. Caso contrário, faça o checkout diretamente."
sleep 5
exit
else
# Faz o checkout do projeto e instala o módulo
echo
echo "Fazendo o checkout do projeto..."
echo
sleep 2
git clone git://github.com/jacobian/django-googlecharts.git
echo
echo "Iniciando a instalacao..."
echo
sleep 2
cd django-googlecharts
python setup.py install
# Instalando o django-geraldo
echo
echo "Fazendo o checkout do projeto django-geraldo..."
echo
sleep 2
git clone https://github.com/marinho/geraldo.git
echo
echo "Instalando o django-geraldo..."
echo
cd geraldo
python setup.py install
cp -Rvf reporting geraldo /usr/local/lib/python2.7/site-packages
fi
else
echo
echo "O arquivo requirements.txt não existe. Verifique se está na mesma pasta do arquivo de instalação do SIGI."
sleep 5
exit
fi
fi

23
sigi/admin/filterspecs.py

@ -1,10 +1,31 @@
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec, BooleanFieldFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from sigi.apps.contatos.models import UnidadeFederativa
from abc import ABCMeta
class IsActiveFilterSpec(BooleanFieldFilterSpec):
"""
Adds filtering by user is_active attr in the admin filter sidebar
my_model_user_field.is_active__filter = True
"""
def __init__(self, f, request, params, model, model_admin):
super(IsActiveFilterSpec, self).__init__(f, request, params, model,
model_admin)
self.lookup_kwarg = '%s__is_active__exact' % f.name
self.lookup_kwarg2 = '%s__is_active__isnull' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None)
def title(self):
return _('active')
# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'is_active__filter', False),
IsActiveFilterSpec))
class AlphabeticFilterSpec(ChoicesFilterSpec):
"""
Adds filtering by first char (alphabetic style) of values in the admin

32
sigi/apps/casas/admin.py

@ -2,8 +2,8 @@
from django.contrib import admin
from django.contrib.contenttypes import generic
from sigi.apps.casas.forms import CasaLegislativaForm
from sigi.apps.casas.models import CasaLegislativa
from sigi.apps.contatos.models import Contato, Telefone
from sigi.apps.casas.models import CasaLegislativa, Presidente, Funcionario
from sigi.apps.contatos.models import Telefone
from sigi.apps.convenios.models import Projeto, Convenio, EquipamentoPrevisto, Anexo
from django.http import HttpResponse, HttpResponseRedirect
from sigi.apps.casas.reports import CasasLegislativasLabels, CasasLegislativasReport
@ -13,14 +13,23 @@ from sigi.apps.casas.views import report_complete, labels_report, export_csv, \
adicionar_casas_carrinho
from sigi.apps.utils import queryset_ascii
class ContatosInline(generic.GenericTabularInline):
model = Contato
extra = 2
raw_id_fields = ('municipio',)
class TelefonesInline(generic.GenericTabularInline):
model = Telefone
extra = 2
extra = 1
class PresidenteInline(admin.StackedInline):
model = Presidente
exclude = ['cargo','funcao']
extra = 1
max_num = 1
inlines = (TelefonesInline)
class FuncionariosInline(admin.StackedInline):
model = Funcionario
extra = 1
inlines = (TelefonesInline)
def queryset(self, request):
return self.model.objects.exclude(cargo="Presidente")
class ConveniosInline(admin.TabularInline):
model = Convenio
@ -32,16 +41,15 @@ class CasaLegislativaAdmin(admin.ModelAdmin):
change_form_template = 'casas/change_form.html'
change_list_template = 'casas/change_list.html'
actions = ['adicionar_casas',]
inlines = (TelefonesInline, ContatosInline, ConveniosInline)
list_display = ('nome','municipio','presidente','logradouro')
inlines = (TelefonesInline, PresidenteInline, FuncionariosInline, ConveniosInline)
list_display = ('nome','municipio','logradouro')
list_display_links = ('nome',)
list_filter = ('tipo', 'municipio')
ordering = ('nome','municipio__uf')
queyrset = queryset_ascii
fieldsets = (
(None, {
'fields': ('tipo', 'nome', 'telefone', 'cnpj',
'presidente'),
'fields': ('tipo', 'nome', 'cnpj',)
}),
('Endereço', {
'fields': ('logradouro', 'bairro', 'municipio', 'cep', 'pagina_web','email'),

6
sigi/apps/casas/forms.py

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
from django import forms
from django.contrib.localflavor.br.forms import BRCNPJField, BRZipCodeField
from django.contrib.localflavor.br.forms import BRZipCodeField
from sigi.apps.casas.models import CasaLegislativa
class CasaLegislativaForm(forms.ModelForm):
#cnpj = BRCNPJField(
# label='CNPJ',
@ -14,3 +15,6 @@ class CasaLegislativaForm(forms.ModelForm):
class Meta:
model = CasaLegislativa
def clean(self):
print self.cleaned_data['bairro']

56
sigi/apps/casas/models.py

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.contenttypes import generic
from sigi.apps.mesas.models import MesaDiretora, MembroMesaDiretora
from sigi.apps.parlamentares.models import Parlamentar
from sigi.apps.utils import SearchField
@ -35,7 +34,6 @@ class CasaLegislativa(models.Model):
tipo = models.ForeignKey(TipoCasaLegislativa, verbose_name="Tipo")
cnpj = models.CharField('CNPJ', max_length=32, blank=True)
observacoes = models.TextField(u'observações', blank=True)
presidente = models.CharField('Presidente', max_length=150, blank=True)
# Informações de contato
logradouro = models.CharField(
@ -56,7 +54,6 @@ class CasaLegislativa(models.Model):
blank=True,
verify_exists=False
)
telefone = models.CharField('Telefone', max_length=100, blank=True)
telefones = generic.GenericRelation('contatos.Telefone')
foto = models.ImageField(
@ -68,7 +65,6 @@ class CasaLegislativa(models.Model):
foto_largura = models.SmallIntegerField(editable=False, null=True)
foto_altura = models.SmallIntegerField(editable=False, null=True)
contatos = generic.GenericRelation('contatos.Contato')
class Meta:
ordering = ('nome',)
@ -78,3 +74,55 @@ class CasaLegislativa(models.Model):
def __unicode__(self):
return self.nome
class Funcionario(models.Model):
""" Modelo para registrar contatos vinculados às
Casas Legislativas
"""
SETOR_CHOICES = [
("presidencia","Presidencia"),
("infraestrutura_fisica","Infraestrutura Física"),
("estrutura_de_ti","Estrutura de TI"),
("organizacao_do_processo_legislativo","Organização do Processo Legislativo"),
("estrutura_de_comunicacao_social","Estrutura de Comunicação Social"),
("estrutura_de_recursos_humanos","Estrutura de Recursos Humanos"),
("estrutura_de_recursos_humanos","Estrutura de Recursos Humanos"),
("estrutura_de_secretaria","Estrutura de Secretaria"),
("outros","Outros"),
]
casa_legislativa = models.ForeignKey(CasaLegislativa)
nome = models.CharField('nome completo', max_length=60, blank=True)
nome.alphabetic_filter = True
nota = models.CharField(max_length=70, null=True, blank=True)
email = models.EmailField('e-mail', null=True, blank=True)
telefones = generic.GenericRelation('contatos.Telefone')
endereco = generic.GenericRelation('contatos.Endereco')
cargo = models.CharField(max_length=100, null=True, blank=True)
funcao = models.CharField(u'função', max_length=100, null=True, blank=True)
setor = models.CharField(max_length=100, choices = SETOR_CHOICES, default="outros")
tempo_de_servico = models.CharField(u'tempo de serviço', max_length=50, null=True, blank=True)
class Meta:
ordering = ('nome',)
verbose_name = 'contato Casa Legislativa'
verbose_name_plural = 'contatos Casas Legislativa'
def __unicode__(self):
return self.nome
class PresidenteManager(models.Manager):
def get_query_set(self):
qs = super(PresidenteManager, self).get_query_set()
qs = qs.filter(cargo='Presidente')
return qs
class Presidente(Funcionario):
class Meta:
proxy = True
objects = PresidenteManager()
def save(self, *args, **kwargs):
self.cargo = 'Presidente'
self.setor = 'presidencia'
return super(Presidente, self).save(*args, **kwargs)

8
sigi/apps/contatos/admin.py

@ -32,11 +32,11 @@ class MunicipioAdmin(admin.ModelAdmin):
'codigo_microrregiao', 'uf__sigla')
class TelefoneAdmin(admin.ModelAdmin):
list_display = ('codigo_area', 'numero', 'tipo', 'nota')
list_display_links = ('codigo_area', 'numero')
list_filter = ('codigo_area', 'tipo')
list_display = ('numero', 'tipo', 'nota')
list_display_links = ('numero',)
list_filter = ('tipo',)
radio_fields = {'tipo': admin.VERTICAL}
search_fields = ('codigo_area', 'numero', 'tipo', 'nota')
search_fields = ('numero', 'tipo', 'nota')
class ContatoAdmin(admin.ModelAdmin):
list_display = ('nome', 'nota', 'email', 'municipio')

24
sigi/apps/contatos/models.py

@ -116,22 +116,17 @@ class Telefone(models.Model):
('X', 'Fax'),
('I', 'Indefinido'),
)
codigo_area = models.CharField(
'código de área',
max_length=4,
help_text='Exemplo: <em>31</em>.',
blank=True
)
numero = models.CharField(
'número',
max_length=64, # TODO: diminuir tamanho de campo após migração de dados
help_text='Somente números.'
help_text='Exemplo: <em>(31)8851-9898</em>.',
)
tipo = models.CharField(
max_length=1,
choices=TELEFONE_CHOICES,
default= 'I'
)
nota = models.CharField(max_length=70, blank=True)
nota = models.CharField(max_length=70, null=True, blank=True)
# guarda o tipo do objeto (classe) vinculado a esse registro
content_type = models.ForeignKey(ContentType)
@ -140,16 +135,11 @@ class Telefone(models.Model):
content_object = generic.GenericForeignKey('content_type', 'object_id')
class Meta:
ordering = ('codigo_area', 'numero')
# desabilitado para facilitar a migração de dados
# TODO: voltar quando estiver em produção
#unique_together = ('codigo_area', 'numero', 'tipo')
ordering = ('numero',)
unique_together = ('numero', 'tipo')
def __unicode__(self):
if self.codigo_area:
return "(%s) %s" % (unicode(self.codigo_area), unicode(self.numero))
else:
return unicode(self.numero)
return unicode(self.numero)
class Contato(models.Model):
""" Modelo generico para registrar contatos vinculados aos
@ -272,6 +262,6 @@ class Endereco(models.Model):
verbose_name_plural = u'endereços'
def __unicode__(self):
return self.tipo + ' ' + seld.logradouro + ', ' + self.numero \
return self.tipo + ' ' + self.logradouro + ', ' + self.numero \
+ ' ' + self.complemento + ' - ' + self.bairro

74
sigi/apps/diagnosticos/admin.py

@ -1,32 +1,94 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from django.contrib import admin
from eav.admin import BaseEntityAdmin, BaseSchemaAdmin
from sigi.apps.diagnosticos.models import Diagnostico, Pergunta, Escolha, Equipe, Anexo
from sigi.apps.diagnosticos.models import Diagnostico, Pergunta, Escolha, Equipe, Anexo, Categoria
from sigi.apps.diagnosticos.forms import DiagnosticoForm
"""
Actions do Admin
"""
def publicar_diagnostico(self, request, queryset):
for registro in queryset:
diagnostico = Diagnostico.objects.get(pk=registro.id)
diagnostico.publicado = True
diagnostico.data_publicacao= datetime.now()
diagnostico.save()
# Enviando o email avisando que o diagnóstico foi publicado
diagnostico.email_diagnostico_publicado(diagnostico.responsavel.email_pessoal, request.get_host())
self.message_user(request, "Diagnóstico(s) publicado(s) com sucesso!")
publicar_diagnostico.short_description = u"""
Definir diagnósticos como publicado"""
def despublicar_diagnostico(self, request, queryset):
queryset.update(publicado=False)
despublicar_diagnostico.short_description = u"""
Definir diagnósticos como não publicado"""
class EquipeInline(admin.TabularInline):
model = Equipe
extra = 4
class AnexosInline(admin.TabularInline):
model = Anexo
extra = 2
exclude = ['data_pub',]
exclude = ['data_pub', ]
class AnexoAdmin(admin.ModelAdmin):
date_hierarchy = 'data_pub'
exclude = ['data_pub',]
exclude = ['data_pub', ]
list_display = ('arquivo', 'descricao', 'data_pub', 'diagnostico')
raw_id_fields = ('diagnostico',)
search_fields = ('descricao', 'diagnostico__id', 'arquivo',
'diagnostico__casa_legislativa__nome')
class DiagnosticoAdmin(BaseEntityAdmin):
form = DiagnosticoForm
actions = [publicar_diagnostico, despublicar_diagnostico]
inlines = (EquipeInline, AnexosInline)
search_fields = ('casa_legislativa__nome', 'responsavel',)
list_display = ('casa_legislativa', 'data_visita_inicio', 'data_visita_fim', 'responsavel', 'publicado')
list_filter = ('publicado', 'data_publicacao', 'data_visita_inicio', 'data_visita_fim')
raw_id_fields = ('casa_legislativa',)
eav_fieldsets = [
(u'00. Identificação do Diagnóstico', {'fields': ('responsavel', 'data_visita_inicio', 'data_visita_fim',)}),
(u'01. Identificação da Casa Legislativa', {'fields': ('casa_legislativa',)}),
(u'02. Identificação de Competências da Casa Legislativa', {'fields': ()})
]
# popula o eav fieldsets ordenando as categorias e as perguntas
# para serem exibidas no admin
for categoria in Categoria.objects.all():
# ordena as perguntas pelo title e utiliza o name no fieldset
perguntas_by_title = [(p.title, p.name) for p in categoria.perguntas.all()]
perguntas = [pergunta[1] for pergunta in sorted(perguntas_by_title)]
eav_fieldsets.append((categoria, {
'fields': tuple(perguntas),
'classes': ['collapse']
}))
class PerguntaAdmin (BaseSchemaAdmin):
search_fields = ('title', 'help_text', 'name',)
list_display = ('title', 'categoria', 'datatype', 'help_text', 'required')
list_filter = ('datatype', 'categoria', 'required')
class EscolhaAdmin(admin.ModelAdmin):
search_fields = ('title',)
list_display = ('title', 'schema', 'schema_to_open')
raw_id_fields = ('schema', 'schema_to_open')
ordering = ('schema', 'title')
admin.site.register(Diagnostico, DiagnosticoAdmin)
admin.site.register(Pergunta, BaseSchemaAdmin)
admin.site.register(Escolha)
admin.site.register(Pergunta, PerguntaAdmin)
admin.site.register(Escolha, EscolhaAdmin)
admin.site.register(Anexo, AnexoAdmin)
admin.site.register(Categoria)

23
sigi/apps/diagnosticos/decorators.py

@ -0,0 +1,23 @@
# -*- coding: utf8 -*-
from django.template import RequestContext
from django.shortcuts import render_to_response
from sigi.apps.diagnosticos.models import Diagnostico
def validate_diagnostico(func):
def decorator(request, id_diagnostico, *args, **kwargs):
""" Retorna 404 caso o diagnostico esteja publicado
ou o usuario nao seja um membro da equipe
"""
try:
diagnostico = Diagnostico.objects.filter(publicado=False).get(pk=id_diagnostico)
if (request.user.servidor in diagnostico.membros):
# continua o processamento normal da view
return func(request, id_diagnostico, *args, **kwargs)
except Diagnostico.DoesNotExist:
pass
# renderiza a pagina de 404
context = RequestContext(request, {})
return render_to_response('mobile/404.html', context)
return decorator

1
sigi/apps/diagnosticos/fixtures/initial_data.json

File diff suppressed because one or more lines are too long

145
sigi/apps/diagnosticos/forms.py

@ -1,5 +1,148 @@
from models import Diagnostico
# -*- coding: utf8 -*-
from copy import deepcopy
from django import forms
from django.forms.forms import BoundField
from django.forms import (BooleanField, CharField, DateField,
FloatField, ModelChoiceField, Textarea,
ModelMultipleChoiceField)
from django.contrib.contenttypes.generic import generic_inlineformset_factory
from sigi.apps.casas.models import CasaLegislativa, Funcionario
from sigi.apps.contatos.models import Telefone
from sigi.apps.diagnosticos.models import Diagnostico
from sigi.apps.diagnosticos.widgets import EavCheckboxSelectMultiple, EavRadioSelect
from eav.forms import BaseDynamicEntityForm
from eav.fields import RangeField
class DiagnosticoForm(BaseDynamicEntityForm):
"""Classe responsável por contruir o formulário,
vinculando ao modelo Diagnostico
"""
model = Diagnostico
class DiagnosticoMobileForm(BaseDynamicEntityForm):
"""Classe responsável por construir o formulário
para ser usado no ambiente mobile, a partir do
do modelo Diagnostico, como também organizar sua
estrutura via categorias.
"""
FIELD_CLASSES = {
'text': CharField,
'float': FloatField,
'date': DateField,
'bool': BooleanField,
'one': ModelChoiceField,
'many': ModelMultipleChoiceField,
'range': RangeField,
}
FIELD_EXTRA = {
'one': {'widget': EavRadioSelect},
'many': {'widget': EavCheckboxSelectMultiple},
}
FIELD_WIDGET = {
'consideracoes_gerais' : {'widget': Textarea},
'descreva_5_cursos_prioritarios_para_treinamento_de_parlamentares_da_camara_municipal' : {'widget': Textarea},
'descreva_5_cursos_prioritarios_para_treinamento_de_servidores_da_camara_municipal' : {'widget': Textarea},
'sugestoes_para_a_area_de_capacitacao' : {'widget': Textarea},
'sugestoes_para_a_area_de_comunicacao' : {'widget': Textarea},
'sugestoes_para_a_area_de_informacao' : {'widget': Textarea},
'sugestoes_para_a_area_de_ti' : {'widget': Textarea},
}
class Meta:
model = Diagnostico
def __init__(self, data=None, category=None, *args, **kwargs):
super(BaseDynamicEntityForm, self).__init__(data, *args, **kwargs)
self._build_dynamics_fields(category)
def __iter__(self):
# ordena os campos do formulario usando o atributo label
fields_by_label = [(field.label, name, field) for name, field in self.fields.items()]
for label, name, field in sorted(fields_by_label):
yield BoundField(self, field, name)
def _build_dynamics_fields(self, category):
"""Método da classe ``BaseDynamicEntityForm`` sobrescrita,
para que as perguntas sejam agrupadas dentro das suas
categorias.
* category = ID da Categoria
"""
# Caso seja as duas primeiras categorias, utilize
# os campos do modelo
if int(category) in (0, 1, ):
self.fields = deepcopy(self.base_fields)
else:
self.fields = dict()
# Se determinada pergunta é da categoria pesquisada,
# então, gere o campo no formulário.
for schema in self.instance.get_schemata():
if not schema.categoria_id == int(category):
continue
defaults = {
'label': schema.title,
'required': schema.required,
'help_text': schema.help_text,
}
datatype = schema.datatype
if datatype == schema.TYPE_MANY:
choices = getattr(self.instance, schema.name)
defaults.update({'queryset': schema.get_choices(),
'initial': [x.pk for x in choices]})
elif datatype == schema.TYPE_ONE:
choice = getattr(self.instance, schema.name)
defaults.update({'queryset': schema.get_choices(),
'initial': choice.pk if choice else None,
# if schema is required remove --------- from ui
'empty_label': None if schema.required else u"---------"})
extra = self.FIELD_EXTRA.get(datatype, {})
extra.update(self.FIELD_WIDGET.get(schema.name, {}))
if hasattr(extra, '__call__'):
extra = extra(schema)
defaults.update(extra)
MappedField = self.FIELD_CLASSES[datatype]
self.fields[schema.name] = MappedField(**defaults)
# fill initial data (if attribute was already defined)
value = getattr(self.instance, schema.name)
if value and not datatype in (schema.TYPE_ONE, schema.TYPE_MANY): # choices are already done above
self.initial[schema.name] = value
class CasaLegislativaMobileForm(forms.ModelForm):
class Meta:
model = CasaLegislativa
fields = ('cnpj', 'logradouro', 'bairro', 'cep', 'email', 'pagina_web')
class TelefoneMobileForm(forms.ModelForm):
pass
class Meta:
model = Telefone
fields = ('numero', 'tipo')
class FuncionariosMobileForm(forms.ModelForm):
TelefoneFormSet = generic_inlineformset_factory(Telefone, TelefoneMobileForm, extra=1, can_delete=False)
def __init__(self, data=None, prefix=None, instance=None, *args, **kwargs):
super(FuncionariosMobileForm, self).__init__(data, prefix=prefix, instance=instance, *args, **kwargs)
self.telefones = self.TelefoneFormSet(data, prefix=prefix, instance=instance)
def is_valid(self):
return self.telefones.is_valid() and super(FuncionariosMobileForm, self).is_valid()
def save(self, commit=True):
self.telefones.save(commit)
return super(FuncionariosMobileForm, self).save(commit)
class Meta:
model = Funcionario
fields = ('nome', 'email', 'cargo', 'funcao', 'tempo_de_servico')

153
sigi/apps/diagnosticos/models.py

@ -1,59 +1,141 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from django.db import models
from sigi.apps.casas.models import CasaLegislativa
from sigi.apps.utils import SearchField
from sigi.apps.utils.email import enviar_email
from eav.models import BaseChoice, BaseEntity, BaseSchema, BaseAttribute
class Diagnostico(BaseEntity):
""" Modelo para representar unm diagnostico realizado
em uma Casa Legislativa
"""
casa_legislativa = models.ForeignKey(
'casas.CasaLegislativa',
verbose_name='Casa Legislativa'
)
verbose_name='Casa Legislativa')
# campo de busca em caixa baixa e sem acento
search_text = SearchField(field_names=['casa_legislativa'])
casa_legislativa.convenio_uf_filter = True
casa_legislativa.convenio_cl_tipo_filter = True
data_visita = models.DateField(
'data da visita',
data_visita_inicio = models.DateField(
u'data inicial da visita',
null=True,
blank=True,
)
data_questionario = models.DateField(
'data do questionario',
data_visita_fim = models.DateField(
u'data final da visita',
null=True,
blank=True,
help_text='Convênio firmado.'
)
data_relatorio_questionario = models.DateField(
'data do relatório do questionario',
null=True,
blank=True
)
data_termo_aceite = models.DateField(
'Equipadas',
publicado = models.BooleanField(default=False)
data_publicacao = models.DateField(
u'data de publicação do diagnóstico',
null=True,
blank=True,
help_text='Equipamentos recebidos.'
)
responsavel = models.ForeignKey('servidores.Servidor',
verbose_name=u'responsável')
class Meta:
verbose_name, verbose_name_plural = u'diagnóstico', u'diagnósticos'
@property
def membros(self):
""" Retorna a lista de membros do diagnostico,
isto é responsavel + equipe
"""
membros = set([self.responsavel])
for equipe in self.equipe_set.all():
membros.add(equipe.membro)
return list(membros)
@property
def contatos_respondidos(self):
"""Retorna uma lista de contatos que foram
respondidos
"""
return list(self.casa_legislativa.funcionario_set.all())
@property
def categorias_respondidas(self):
""" Retorna uma listas das categorias dinamicas que tem
ao menos uma resposta
"""
# unifica as categorias das perguntas dessas respostas
categoria_com_respostas = set([r.schema.categoria for r in self._get_respostas()])
return list(categoria_com_respostas)
def _get_respostas(self):
# obtem todas as respostas dinamicas desse diagnostico
respostas = Resposta.objects.filter(entity_id=self.id).all()
# remove as respostas nulas ou em branco
return [r for r in respostas if r._get_value()]
def email_diagnostico_publicado(self, from_email, host):
"""Enviando email quando o diagnóstico for publicado. Os
argumentos acima são:
* from_email - Email de remetente
* host - O Host do sistema, para ser usado na
construção do endereço do diagnóstico
"""
enviar_email(from_email, u"Diagnóstico publicado",
'diagnosticos/email_diagnostico_publicado.txt',
{
'responsavel': self.responsavel.nome_completo,
'casa_legislativa': self.casa_legislativa,
'data_diagnostico': self.data_questionario,
'host': host,
'url_diagnostico': self.get_absolute_url(),
'status': u"Publicado",
})
def email_diagnostico_alterado(self, from_email, host):
"""Enviando email quando o status do diagnóstico
for alterado. Os argumentos acima são:
* from_email - Email do destinatário
* host - O Host do sistema, para ser usado na
construção do endereço do diagnóstico
"""
enviar_email(from_email, u"Diagnóstico alterado",
'diagnosticos/email_diagnostico_alterado.txt',
{
'servidor': self.responsavel.nome_completo,
'casa_legislativa': self.casa_legislativa,
'data_diagnostico': self.data_questionario,
'host': host,
'url_diagnostico': self.get_absolute_url,
'status': "Alterado",
})
@classmethod
def get_schemata_for_model(self):
return Pergunta.objects.all()
def __unicode__(self):
return str(self.casa_legislativa)
return str(self.casa_legislativa).decode('utf8')
def get_absolute_url(self):
return "/sigi/diagnosticos/diagnostico/%i/" % (self.id, )
class Categoria(models.Model):
""" Modelo para representar a categoria de uma pergunta
e sua ordem na hora de exibir no formulário
"""
nome= models.CharField(max_length=50)
ordem = models.PositiveSmallIntegerField(blank=True, null=True)
nome = models.CharField(max_length=255)
class Meta:
ordering = ('nome',)
def __unicode__(self):
return self.nome
class Pergunta(BaseSchema):
""" Modelo que representa uma pergunta no questionário
@ -61,37 +143,51 @@ class Pergunta(BaseSchema):
Uma pergunta tem o nome e o tipo da resposta
"""
categoria = models.ForeignKey(Categoria,blank=True, null=True)
categoria = models.ForeignKey(Categoria, related_name='perguntas')
class Meta:
ordering = ('title',)
verbose_name, verbose_name_plural = 'pergunta', 'perguntas'
class Escolha(BaseChoice):
""" Perguntas de multiplas escolhas tem as opções
cadastradas neste modelo
"""
schema = models.ForeignKey(Pergunta, related_name='choices', verbose_name='pergunta')
schema = models.ForeignKey(Pergunta,
related_name='choices', verbose_name='pergunta')
schema_to_open = models.ForeignKey(Pergunta, related_name='',
verbose_name='pergunta para abrir', blank=True, null=True)
class Meta:
verbose_name, verbose_name_plural = 'escolha', 'escolhas'
class Resposta(BaseAttribute):
""" Modelo para guardar as respostas das perguntas
de um diagnosico
"""
schema = models.ForeignKey(Pergunta, related_name='attrs', verbose_name='pergunta')
choice = models.ForeignKey(Escolha, verbose_name='escolha', blank=True, null=True)
schema = models.ForeignKey(Pergunta, related_name='attrs',
verbose_name='pergunta')
choice = models.ForeignKey(Escolha, verbose_name='escolha',
blank=True, null=True)
class Meta:
verbose_name, verbose_name_plural = 'resposta', 'respostas'
class Equipe(models.Model):
""" Modelo que representa a equipe de um diagnóstico
"""
diagnostico = models.ForeignKey(Diagnostico)
membro = models.ForeignKey('servidores.Servidor')
# verdadeiro se o servidor é repsonsável por chefiar a equipe
is_chefe = models.BooleanField()
class Meta:
verbose_name, verbose_name_plural = 'equipe', 'equipe'
def __unicode__(self):
return str(self.id)
return str(self.membro)
class Anexo(models.Model):
""" Modelo para representar os documentos levantados
@ -100,14 +196,11 @@ class Anexo(models.Model):
diagnostico = models.ForeignKey(Diagnostico, verbose_name=u'diagnóstico')
arquivo = models.FileField(upload_to='apps/diagnostico/anexo/arquivo',)
descricao = models.CharField('descrição', max_length='70')
data_pub = models.DateTimeField(
'data da publicação do anexo',
default=datetime.now
)
data_pub = models.DateTimeField('data da publicação do anexo',
default=datetime.now)
class Meta:
ordering = ('-data_pub',)
def __unicode__(self):
return unicode(self.arquivo.name)

15
sigi/apps/diagnosticos/tests.py

@ -0,0 +1,15 @@
# -*- coding: utf8 -*-
from django.test import TestCase
class DiagnosticosViewsTest(TestCase):
"""Testes feitos para verificar o funcionamento
do view de diagnósticos.
"""
def test_diagnostico_list_success(self):
response = self.client.get('/sigi/mobile/diagnosticos')
self.assertEquals(200, response.status_code)
self.assertTemplateUsed(response, 'diagnosticos/diagnosticos_list.html')

35
sigi/apps/diagnosticos/urls.py

@ -0,0 +1,35 @@
# -*- coding: utf8 -*-
from django.conf.urls.defaults import patterns, url
LOGIN_REDIRECT_URL = '/sigi/mobile/diagnosticos/login'
urlpatterns = patterns('',
# Lista de Diagnósticos
url(r'^$', 'sigi.apps.diagnosticos.views.lista', name='lista_diagnosticos'),
# Login do Diagnóstico
url(r'^login/$', 'django.contrib.auth.views.login', {'template_name':
'diagnosticos/diagnosticos_login.html'}, name='login'),
# Logout do Diagnóstico
url(r'^logout/$', 'django.contrib.auth.views.logout',
{'next_page': LOGIN_REDIRECT_URL}, name='logout'),
# Lista de Categorias
url(r'^(?P<id_diagnostico>\d+)/categorias/$', 'sigi.apps.diagnosticos.views.categorias', name='lista_categorias'),
# Detalhes da Categoria da Casa Legislativa
url(r'^(?P<id_diagnostico>\d+)/categorias/1/$',
'sigi.apps.diagnosticos.views.categoria_casa_legislativa',
name='detalhes_categoria_casa_legislativa'),
# Detalhes da Categoria de Contatos
url(r'^(?P<id_diagnostico>\d+)/categorias/2/$',
'sigi.apps.diagnosticos.views.categoria_contatos',
name='detalhes_categoria_contatos'),
# Detalhes de Categorias Dinamicas
url(r'^(?P<id_diagnostico>\d+)/categorias/(?P<id_categoria>\d+)/$',
'sigi.apps.diagnosticos.views.categoria_detalhes',
name='detalhes_categoria'),
)

187
sigi/apps/diagnosticos/views.py

@ -0,0 +1,187 @@
# -*- coding: utf8 -*-
from django.http import HttpResponse
from django.utils import simplejson
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.decorators.cache import never_cache
from sigi.apps.diagnosticos.urls import LOGIN_REDIRECT_URL
from sigi.apps.utils.decorators import login_required
from sigi.apps.diagnosticos.decorators import validate_diagnostico
from sigi.apps.diagnosticos.models import Diagnostico, Categoria
from sigi.apps.casas.models import Funcionario
from sigi.apps.diagnosticos.forms import (DiagnosticoMobileForm,
CasaLegislativaMobileForm, FuncionariosMobileForm)
@never_cache
@login_required(login_url=LOGIN_REDIRECT_URL)
def lista(request):
"""Consulta os diagnosticos do servidor logado,
que contenham o status de não publicado.
"""
servidor = request.user.servidor
diagnosticos = servidor.diagnosticos
context = RequestContext(request, {'diagnosticos': diagnosticos})
return render_to_response('diagnosticos/diagnosticos_list.html', context)
@never_cache
@login_required(login_url=LOGIN_REDIRECT_URL)
@validate_diagnostico
def categorias(request, id_diagnostico):
"""Consulta as categorias do diagnostico selecionado
a partir da sua identificação
"""
categorias = Categoria.objects.all()
diagnostico = Diagnostico.objects.get(pk=id_diagnostico)
# Estilizando a lista de categorias para que ajude a identificar
# qual categoria foi a ultima a ser usada, como também as outras
# que ainda não foram acessadas
ultima_categoria = request.session.get('ultima_categoria', 0)
context = RequestContext(request, {'categorias': categorias,
'diagnostico': diagnostico, 'ultima_categoria': ultima_categoria})
return render_to_response('diagnosticos/diagnosticos_categorias_list.html',
context)
@never_cache
@login_required(login_url=LOGIN_REDIRECT_URL)
@validate_diagnostico
def categoria_detalhes(request, id_diagnostico, id_categoria):
"""Captura as perguntas da categoria
selecionada. Durante o preenchimento das perguntas, o camada
template do projeto, vai requisitar a inserção dos campos via
AJAX a cada mudança de pergunta
Caso alguma inserção não passe na validação do formulário em
questão, será enviado as mensagens de erro no formato JSON,
para que a camada de template do projeto trate-as de forma adequada.
"""
# Grava na sessão a categoria atual, para destacar que
# era foi a última visitada.
request.session['ultima_categoria'] = int(id_categoria)
try:
categoria = Categoria.objects.get(pk=id_categoria)
except Categoria.DoesNotExist:
context = RequestContext(request)
return render_to_response('mobile/404.html', context)
diagnostico = Diagnostico.objects.filter(publicado=False).get(pk=id_diagnostico)
if request.method == "POST":
form = DiagnosticoMobileForm(request.POST,
instance=diagnostico, category=id_categoria)
if form.is_valid():
form.save()
resposta = {
'mensagem': 'sucesso'
}
else:
# Montando a estrutura das mensagens de erro no formato JSON
resposta = {
'mensagem': 'erro',
'erros': form.errors
}
json = simplejson.dumps(resposta)
return HttpResponse(json, mimetype="application/json")
else:
form = DiagnosticoMobileForm(instance=diagnostico,
category=id_categoria)
context = RequestContext(request, {'form': form, 'categoria': categoria,
'diagnostico': diagnostico})
return render_to_response('diagnosticos/diagnosticos_categorias_form.html',
context)
@never_cache
@login_required(login_url=LOGIN_REDIRECT_URL)
@validate_diagnostico
def categoria_casa_legislativa(request, id_diagnostico):
# Grava na sessão a categoria atual, para destacar que
# era foi a última visitada.
request.session['ultima_categoria'] = 1
diagnostico = Diagnostico.objects.get(pk=id_diagnostico)
casa_legislativa = diagnostico.casa_legislativa
if request.method == "POST":
form = CasaLegislativaMobileForm(request.POST,
instance=casa_legislativa)
if form.is_valid():
form.save()
resposta = {
'mensagem': 'sucesso'
}
else:
# Montando a estrutura das mensagens de erro no formato JSON
resposta = {
'mensagem': 'erro',
'erros': form.errors
}
json = simplejson.dumps(resposta)
return HttpResponse(json, mimetype="application/json")
else:
form = CasaLegislativaMobileForm(instance=casa_legislativa)
context = RequestContext(request, {'form': form,
'diagnostico': diagnostico, 'casa_legislativa': casa_legislativa})
return render_to_response(
'diagnosticos/diagnosticos_categoria_casa_legislativa_form.html',
context)
@never_cache
@login_required(login_url=LOGIN_REDIRECT_URL)
@validate_diagnostico
def categoria_contatos(request, id_diagnostico):
# Grava na sessão a categoria atual, para destacar que
# era foi a última visitada.
request.session['ultima_categoria'] = 2
diagnostico = Diagnostico.objects.get(pk=id_diagnostico)
casa_legislativa = diagnostico.casa_legislativa
funcionarios = [casa_legislativa.funcionario_set.get_or_create(setor=n)
for n, l in Funcionario.SETOR_CHOICES]
if request.method == "POST":
forms = [FuncionariosMobileForm(
request.POST, prefix=f.setor, instance=f) for f, c in funcionarios]
resposta = {
'mensagem': 'sucesso',
'erros' : {}
}
# valida e salva um formulario por vez
for form in forms:
if form.is_valid():
form.save()
else:
# Montando a estrutura das mensagens de erro no formato JSON
resposta['mensagem'] = 'erro'
resposta['erros'].update(form.errors)
for form_telefones in form.telefones.forms:
for key, value in form_telefones.errors.iteritems():
key = form_telefones.prefix + "-" + key
resposta['erros'][key] = value
json = simplejson.dumps(resposta)
return HttpResponse(json, mimetype="application/json")
else:
forms = [FuncionariosMobileForm(prefix=f.setor, instance=f)
for f, c in funcionarios]
context = RequestContext(request, {'forms': forms,
'diagnostico': diagnostico, 'casa_legislativa': casa_legislativa})
return render_to_response('diagnosticos/diagnosticos_categoria_contatos_form.html',
context)

61
sigi/apps/diagnosticos/widgets.py

@ -0,0 +1,61 @@
from itertools import chain
from django.forms.widgets import CheckboxInput, CheckboxSelectMultiple, RadioSelect, RadioFieldRenderer, RadioInput
from django.utils.html import conditional_escape
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from sigi.apps.diagnosticos.models import Escolha
class EavCheckboxSelectMultiple(CheckboxSelectMultiple):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<ul>']
str_values = set([force_unicode(v) for v in value])
for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
label_for = u' for="%s"' % final_attrs['id']
# Caso exista uma pergunta para abrir
# adiciona um atripbuto no checkbox
schema_to_open = Escolha.objects.get(pk=option_value).schema_to_open
if schema_to_open:
final_attrs['schema_to_open'] = schema_to_open.name
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
option_value = force_unicode(option_value)
rendered_cb = cb.render(name, option_value)
option_label = conditional_escape(force_unicode(option_label))
output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label))
output.append(u'</ul>')
return mark_safe(u'\n'.join(output))
class EavRadioFieldRenderer(RadioFieldRenderer):
def __iter__(self):
for i, choice in enumerate(self.choices):
final_attrs = self.attrs.copy()
# Caso exista uma pergunta para abrir
# adiciona um atripbuto no checkbox
if choice[0]:
schema_to_open = Escolha.objects.get(pk=choice[0]).schema_to_open
if schema_to_open:
final_attrs['schema_to_open'] = schema_to_open.name
yield RadioInput(self.name, self.value, final_attrs, choice, i)
def __getitem__(self, idx):
choice = self.choices[idx]
final_attrs = self.attrs.copy()
# Caso exista uma pergunta para abrir
# adiciona um atripbuto no checkbox
schema_to_open = Escolha.objects.get(pk=self.value).schema_to_open
if schema_to_open:
final_attrs['schema_to_open'] = schema_to_open.name
return RadioInput(self.name, self.value,final_attrs, choice, idx)
class EavRadioSelect(RadioSelect):
renderer = EavRadioFieldRenderer

2
sigi/apps/servicos/models.py

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.contenttypes import generic
from apps.casas.models import CasaLegislativa
from sigi.apps.casas.models import CasaLegislativa
from datetime import date
class Servico(models.Model):

69
sigi/apps/servidores/admin.py

@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
from django.contrib.contenttypes import generic
from sigi.apps.utils.admin_widgets import AdminImageWidget
from sigi.apps.servidores.models import Servidor, Funcao, Licenca, Ferias
from sigi.apps.contatos.models import Endereco, Telefone
class FuncaoInline(admin.TabularInline):
model = Funcao
extra = 1
from sigi.apps.servidores.forms import FeriasForm, LicencaForm, FuncaoForm
class FuncaoAdmin(admin.ModelAdmin):
form = FuncaoForm
list_display = ('servidor', 'funcao', 'cargo','inicio_funcao', 'fim_funcao')
list_filter = ('inicio_funcao', 'fim_funcao')
search_fields = ('funcao', 'cargo', 'descricao',
@ -16,33 +16,25 @@ class FuncaoAdmin(admin.ModelAdmin):
'servidor__user__email', 'servidor__user__first_name',
'servidor__user__last_name', 'servidor__user__username')
class FeriasInline(admin.TabularInline):
model = Ferias
extra = 1
class FeriasAdmin(admin.ModelAdmin):
form = FeriasForm
list_display = ('servidor', 'inicio_ferias', 'fim_ferias')
list_filter = ('servidor', 'inicio_ferias', 'fim_ferias')
list_filter = ('inicio_ferias', 'fim_ferias')
search_fields = ('obs',
'servidor__nome_completo', 'servidor__obs', 'servidor__apontamentos',
'servidor__user__email', 'servidor__user__first_name',
'servidor__user__last_name', 'servidor__user__username')
class LicencaInline(admin.TabularInline):
model = Licenca
extra = 1
'servidor__nome_completo', 'servidor__email_pesoal',
'servidor__user__email', 'servidor__user__username')
class LicencaAdmin(admin.ModelAdmin):
form = LicencaForm
list_display = ('servidor', 'inicio_licenca', 'fim_licenca')
list_filter = ('servidor', 'inicio_licenca', 'fim_licenca')
search_fields = ('obs',
'servidor__nome_completo', 'servidor__obs', 'servidor__apontamentos',
'servidor__user__email', 'servidor__user__first_name',
'servidor__user__last_name', 'servidor__user__username')
'servidor__nome_completo', 'servidor__email_pesoal',
'servidor__user__email', 'servidor__user__username')
class EnderecoInline(generic.GenericTabularInline):
class EnderecoInline(generic.GenericStackedInline):
model = Endereco
extra = 1
extra = 0
raw_id_fields = ('municipio',)
class TelefonesInline(generic.GenericTabularInline):
@ -50,8 +42,24 @@ class TelefonesInline(generic.GenericTabularInline):
model = Telefone
class ServidorAdmin(admin.ModelAdmin):
list_display = ('nome_completo', 'servico')
list_filter = ('sexo', 'servico')
def is_active(self, servidor):
return servidor.user.is_active
is_active.admin_order_field = 'is_active'
is_active.boolean = True
is_active.short_description = 'ativo'
def queryset(self, request):
qs = super(ServidorAdmin, self).queryset(request)
qs = qs.extra(select={'is_active': """
SELECT auth_user.is_active
FROM auth_user
WHERE auth_user.id = servidores_servidor.user_id
"""})
return qs
list_display = ('nome_completo', 'is_active', 'foto', 'servico')
list_filter = ('user', 'sexo', 'servico',)
search_fields = ('nome_completo', 'obs', 'apontamentos',
'user__email', 'user__first_name',
'user__last_name', 'user__username')
@ -62,20 +70,23 @@ class ServidorAdmin(admin.ModelAdmin):
'fields': ('user',),
}),
('Cadastro', {
'fields': ('nome_completo', 'foto', 'email_pessoal', 'rg', 'cpf', 'sexo', 'data_nascimento', 'matricula', 'ramal')
'fields': ('nome_completo', 'foto', 'email_pessoal', 'rg', 'cpf', 'sexo', 'data_nascimento', 'matricula', 'ramal', 'data_nomeacao', 'ato_numero', 'ato_exoneracao')
}),
('Origem', {
'fields': ('turno',),
'fields': ('turno', 'de_fora'),
}),
(u'Observações', {
'fields': ('apontamentos', 'obs'),
}),
#('Advanced options', {
# 'classes': ('collapse',),
# 'fields': ('enable_comments', 'registration_required', 'template_name')
#}),
)
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)
admin.site.register(Servidor, ServidorAdmin)
admin.site.register(Funcao, FuncaoAdmin)
admin.site.register(Ferias, FeriasAdmin)

60
sigi/apps/servidores/forms.py

@ -0,0 +1,60 @@
# -*- coding: utf8 -*-
from django import forms
from sigi.apps.utils.validators import valida_data, valida_periodo_data
from sigi.apps.servidores.models import Ferias, Licenca, Funcao, Servidor
class FeriasForm(forms.ModelForm):
class Meta:
model = Ferias
def clean(self):
data = self.cleaned_data
if valida_data(data.get('inicio_ferias'), data.get('fim_ferias')):
raise forms.ValidationError(u"""A data de início deve ser menor
que a data final. Verifique novamente""")
return data
class LicencaForm(forms.ModelForm):
class Meta:
model = Licenca
def clean(self):
data = self.cleaned_data
if valida_data(data.get('inicio_licenca'), data.get('fim_licenca')):
raise forms.ValidationError(u"""A data de início deve ser menor
que a data final. Verifique novamente""")
return data
class FuncaoForm(forms.ModelForm):
class Meta:
model = Funcao
def clean(self):
data = self.cleaned_data
if valida_data(data.get('inicio_funcao'), data.get('fim_funcao')):
raise forms.ValidationError(u"""A data de início deve ser menor
que a data final. Verifique
novamente""")
# 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'))
if len(servidor.funcao_set.all()):
if len(servidor.funcao_set.all()) > 1:
funcao_anterior = servidor.funcao_set.all()[1]
elif len(servidor.funcao_set.all()) == 1:
funcao_anterior = servidor.funcao_set.all()[0]
if valida_periodo_data(funcao_anterior.inicio_funcao,
funcao_anterior.fim_funcao, data.get('inicio_funcao'),
data.get('fim_funcao')):
raise forms.ValidationError(u"""Você não pode exercer
uma função no mesmo período que a anterior, como também,
não pode ser entre o período da mesma.""")
return data

0
sigi/apps/servidores/management/__init__.py

0
sigi/apps/servidores/management/commands/__init__.py

233
sigi/apps/servidores/management/commands/migra.py

@ -0,0 +1,233 @@
# coding= utf-8
import sys
import csv
import re
from datetime import datetime
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
from sigi.apps.servidores.models import Servidor, Servico, Subsecretaria, Funcao, Ferias, Licenca
from sigi.apps.contatos.models import Municipio
#Funcao.objects.all().delete()
#Ferias.objects.all().delete()
#Licenca.objects.all().delete()
#for u in User.objects.filter(date_joined__gte=datetime(2011, 12, 9, 10, 58, 49, 83734)).all():
# u.servidor_set.all().delete()
# u.delete()
class MigrationError(Exception):
pass
class Command(BaseCommand):
help = 'Migra usuários do antigo Sistema de RH'
def to_date(self, data):
return datetime.strptime(data, "%Y-%m-%d 00:00:00")
def handle(self, *args, **options):
reader = csv.reader(open("/tmp/pessoal.csv"), delimiter=',', quotechar="\"")
BRASILIA = Municipio.objects.get(codigo_ibge=5300108)
# Read the column names from the first line of the file
fields = reader.next()
for row in reader:
# cria um dict com a primeira e a linha atual
pessoa = zip(fields, row)
p = {}
for (name, value) in pessoa:
p[name] = value.strip()
user = None
if not p['email']:
username = ''
email = ''
elif not ('@interlegis' in p['email']):
username = p['email'].split('@')[0].strip().lower()
email = ''
else:
username = p['email'].split('@')[0].strip().lower()
email = username + '@interlegis.gov.br'
# buscar usuário e servidor da linha atual
try:
# procuro o usuario por email do interlegis
if email:
try: user = User.objects.get(email=email)
except User.DoesNotExist: pass
if not user and username:
try: user = User.objects.get(username=username)
except User.DoesNotExist:
try: user = User.objects.get(username=username + "__")
except User.DoesNotExist: pass
if not user:
if not username:
raise MigrationError
if not email:
# cria um username a partir do email sem
# colidir com os usuarios ldap
username = username + '__'
names = p['nome_completo'].split(' ')
first_name = names[0]
last_name = " ".join(names[1:])
user = User.objects.create(
username = username,
email = email,
first_name = first_name,
last_name = last_name[:30],
is_active= False
)
servidor = user.servidor
except Servidor.DoesNotExist:
servidor = Servidor.objects.create(
user=user,
nome_completo= "%s %s" % (user.first_name, user.last_name)
)
except MigrationError, e:
print ", ".join(row)
continue
# mapeando dados simples
servidor.nome_completo = p['nome_completo']
servidor.cpf = p['cpf']
servidor.rg = p['identidade']
servidor.apelido = p['username']
servidor.matricula = p['matricula']
servidor.ato_exoneracao = p['ato_exoneracao']
servidor.ato_numero = p['ato_numero']
servidor.ramal = p['ramal']
if p['email'] and not '@interlegis' in p['email']:
servidor.email_pessoal= p['email']
if p['inativo']=="-1":
servidor.user.is_active = False
else:
servidor.user.is_active = True
servidor.user.save()
if p['de_fora']=="-1":
servidor.de_fora = True
else:
servidor.de_fora = False
if p['sexo'].upper() == 'M':
servidor.sexo = 'M'
elif p['sexo'].upper() == 'F':
servidor.sexo = 'F'
if p['turno']=="1":
servidor.turno = 'M'
elif p['turno']=="2":
servidor.turno = 'T'
elif p['turno']=="3":
servidor.turno = 'N'
if p['aniversario']:
servidor.data_nascimento = self.to_date(p['aniversario'])
if p['data_nomeacao']:
servidor.data_nomeacao = self.to_date(p['data_nomeacao'])
if p['secretaria_sigla']:
if ' - ' in p['secretaria_nome']:
secretaria_nome = p['secretaria_nome'].split(' - ')[1]
else:
secretaria_nome = p['secretaria_nome']
secretaria = Subsecretaria.objects.get_or_create(
sigla = p['secretaria_sigla'],
nome = secretaria_nome
)[0]
if ' - ' in p['servico_nome']:
servico_nome = p['servico_nome'].split(' - ')[1]
else:
servico_nome = p['servico_nome']
servico = Servico.objects.get_or_create(
sigla = p['servico_sigla'],
nome = servico_nome
)[0]
servico.subsecretaria = secretaria
servico.save()
servidor.servico = servico
if p['telefone']:
try:
t = servidor.telefones.get(numero=p['telefone'])
except:
t = servidor.telefones.create(numero=p['telefone'])
t.tipo = 'F'
t.save()
if p['celular']:
try:
t = servidor.telefones.get(numero=p['celular'])
except:
t = servidor.telefones.create(numero=p['celular'])
t.tipo = 'M'
t.save()
if p['endereco']:
try:
e = servidor.endereco.get(logradouro=p['endereco'])
except:
e = servidor.endereco.create(logradouro=p['endereco'])
e.municipio = BRASILIA
e.bairro = p['cidade'] # bizarro mas é isso mesmo
e.cep = re.sub("\D", "", p['cep'])
e.save()
servidor.apontamentos = p['apontamentos']
servidor.obs = p['obs']
if p['cargo'] or p['funcao']:
funcao = servidor.funcao_set.get_or_create(
funcao = p['funcao'],
cargo = p['cargo'],
)[0]
if p['data_bap_entrada']:
funcao.data_bap_entrada = self.to_date(p['data_bap_entrada'])
if p['data_bap_saida']:
funcao.data_bap_saida = self.to_date(p['data_bap_saida'])
if p['data_entrada']:
funcao.inicio_funcao = self.to_date(p['data_entrada'])
if p['data_saida']:
funcao.fim_funcao = self.to_date(p['data_saida'])
funcao.bap_entrada = p['bap_entrada']
funcao.bap_saida = p['bap_saida']
funcao.save()
if re.search(r'estagi.ri[o|a]',p['cargo'],re.I):
#TODO inserir dados de estagio
pass
if p['inicio_ferias'] and p['final_ferias']:
servidor.ferias_set.get_or_create(
inicio_ferias = self.to_date(p['inicio_ferias']),
fim_ferias = self.to_date(p['final_ferias']),
obs = p['obs_ferias']
)
if p['inicio_licenca'] and p['fim_licenca']:
servidor.licenca_set.get_or_create(
inicio_licenca = self.to_date(p['inicio_licenca']),
fim_licenca = self.to_date(p['fim_licenca']),
obs = p['obs_licenca']
)
servidor.save()

93
sigi/apps/servidores/management/commands/sync_ldap.py

@ -0,0 +1,93 @@
# coding= utf-8
import ldap
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User, Group
from sigi.settings import *
from sigi.apps.servidores.models import Servidor
class Command(BaseCommand):
help = 'Sincroniza Usuários e Servidores com o LDAP'
def handle(self, *args, **options):
self.sync_groups()
self.sync_users()
def get_ldap_groups(self):
filter = "(&(objectclass=Group))"
values = ['cn',]
l = ldap.initialize(AUTH_LDAP_SERVER_URI)
l.protocol_version = ldap.VERSION3
l.simple_bind_s(AUTH_LDAP_BIND_DN.encode('utf-8'),AUTH_LDAP_BIND_PASSWORD)
result_id = l.search(AUTH_LDAP_GROUP, ldap.SCOPE_SUBTREE, filter, values)
result_type, result_data = l.result(result_id, 1)
l.unbind()
return result_data
def get_ldap_users(self):
filter = "(&(objectclass=user))"
values = ['sAMAccountName', 'userPrincipalName', 'givenName', 'sn', 'cn' ]
l = ldap.initialize(AUTH_LDAP_SERVER_URI)
l.protocol_version = ldap.VERSION3
l.simple_bind_s(AUTH_LDAP_BIND_DN.encode('utf-8'),AUTH_LDAP_BIND_PASSWORD)
result_id = l.search(AUTH_LDAP_USER.encode('utf-8'), ldap.SCOPE_SUBTREE, filter, values)
result_type, result_data = l.result(result_id, 1)
l.unbind()
return result_data
def sync_groups(self):
ldap_groups = self.get_ldap_groups()
for ldap_group in ldap_groups:
try: group_name = ldap_group[1]['cn'][0]
except: pass
else:
try: group = Group.objects.get(name=group_name)
except Group.DoesNotExist:
group = Group(name=group_name)
group.save()
print "Group '%s' created." % group_name
print "Groups are synchronized."
def sync_users(self):
ldap_users = self.get_ldap_users()
for ldap_user in ldap_users:
try: username = ldap_user[1]['sAMAccountName'][0]
except: pass
else:
try: email = ldap_user[1]['userPrincipalName'][0]
except: email = ''
try: first_name = ldap_user[1]['givenName'][0]
except: first_name = username
try: last_name = ldap_user[1]['sn'][0][:30]
except: last_name = ''
try: user = User.objects.get(username=username)
except User.DoesNotExist:
user = User.objects.create_user(username, email, username)
user.first_name = first_name
user.last_name = last_name
print "User '%s' created." % username
try: nome_completo = ldap_user[1]['cn'][0]
except: nome_completo = ''
try:
servidor = user.servidor
if not servidor.nome_completo == nome_completo.decode('utf8'):
servidor.nome_completo = nome_completo
print "Servidor '%s' updated." % nome_completo
except Servidor.DoesNotExist:
try: servidor = Servidor.objects.get(nome_completo=nome_completo)
except Servidor.DoesNotExist:
servidor = user.servidor_set.create(nome_completo=nome_completo)
print "Servidor '%s' created." % nome_completo
else:
if not user.email == email.decode('utf8'):
user.email = email
print "User '%s' email updated." % username
if not user.first_name == first_name.decode('utf8'):
user.first_name = first_name
print "User '%s' first name updated." % username
if not user.last_name == last_name.decode('utf8'):
user.last_name = last_name
print "User '%s' last name updated." % username
servidor.user = user
servidor.save()
user.save()
print "Users are synchronized."

62
sigi/apps/servidores/models.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from django.db import models
from django.db.models.signals import post_save
from django.contrib.contenttypes import generic
from django.contrib.auth.models import User
@ -7,10 +8,10 @@ class Subsecretaria(models.Model):
""" Modelo para representação das Subsecretarias do Interlegis
"""
nome = models.CharField(max_length=50)
sigla = models.CharField(max_length=10)
nome = models.CharField(max_length=250, null=True)
sigla = models.CharField(max_length=10, null=True)
# servidor responsavel por dirigir a Subsecretaria
responsavel = models.ForeignKey('servidores.Servidor', related_name='diretor')
responsavel = models.ForeignKey('servidores.Servidor', related_name='diretor', null=True)
class Meta:
ordering = ('nome',)
@ -22,11 +23,11 @@ class Servico(models.Model):
""" Modelo para representação dos Serviços de uma Subsecretaria
"""
nome = models.CharField(max_length=50)
sigla = models.CharField(max_length=10)
subsecretaria = models.ForeignKey(Subsecretaria)
nome = models.CharField(max_length=250, null=True)
sigla = models.CharField(max_length=10, null=True)
subsecretaria = models.ForeignKey(Subsecretaria, null=True)
# servidor responsavel por chefiar o serviço
responsavel = models.ForeignKey('servidores.Servidor', related_name='chefe')
responsavel = models.ForeignKey('servidores.Servidor', related_name='chefe', null=True)
class Meta:
ordering = ('nome',)
@ -56,6 +57,7 @@ class Servidor(models.Model):
# usuario responsavel pela autenticação do servidor no sistema
user = models.ForeignKey(User, unique=True)
user.is_active__filter = True
nome_completo = models.CharField(max_length=128)
nome_completo.alphabetic_filter = True
apelido = models.CharField(max_length=50, blank=True)
@ -87,34 +89,36 @@ class Servidor(models.Model):
blank=True,
null=True,
)
de_fora = models.BooleanField(default=False)
data_nomeacao = models.DateField(u'data de nomeação', blank=True, null=True)
ato_exoneracao = models.CharField(u'ato de exoneração',max_length=150, blank=True, null=True)
ato_numero = models.CharField(u'ato de exoneração',max_length=150, blank=True, null=True)
cpf = models.CharField('CPF', max_length=11, blank=True, null=True)
rg = models.CharField('RG', max_length=25, blank=True, null=True)
obs = models.TextField(u'observação', blank=True, null=True)
apontamentos = models.TextField(u'apontamentos', blank=True, null=True)
# Informações de contato
email_pessoal = models.EmailField('e-mail pessoal', blank=True, null=True)
email_pessoal = models.EmailField('email pessoal', blank=True, null=True)
endereco = generic.GenericRelation('contatos.Endereco')
telefones = generic.GenericRelation('contatos.Telefone')
ramal = models.IntegerField('ramal', blank=True, null=True)
ramal = models.CharField(max_length=25, blank=True, null=True)
class Meta:
ordering = ('nome_completo',)
verbose_name_plural = 'servidores'
def is_chefe():
def is_chefe(self):
""" Verifica se o servidor é chefe ou diretor
"""
pass
def data_entrada():
def data_entrada(self):
""" Verifica a data de entrada da função mais antiga
"""
pass
def data_saida():
def data_saida(self):
""" Verifica a data de saída da função mais recente
de um servidor desativado
@ -122,17 +126,45 @@ class Servidor(models.Model):
"""
pass
@property
def diagnosticos(self):
""" Retorna todos os diagnosticos que este servidor
participa, isto é, como responsavel ou parte da equipe
"""
diagnosticos = set(self.diagnostico_set.filter(publicado=True).all())
for equipe in self.equipe_set.all():
diagnosticos.add(equipe.diagnostico)
return list(diagnosticos)
def __unicode__(self):
return self.nome_completo
# Soluçao alternativa para extender o usuário do django
# Acessa do servidor de um objeto user criando um profile
# baseado nos dados do LDAP
User.servidor = property(lambda user: Servidor.objects.get(user=user))
# Sinal para ao criar um usuário criar um servidor
# baseado no nome contino no LDAP
def create_user_profile(sender, instance, created, **kwargs):
if created:
Servidor.objects.create(
user=instance,
nome_completo= "%s %s" % (instance.first_name, instance.last_name)
)
post_save.connect(create_user_profile, sender=User)
class Funcao(models.Model):
""" Modelo para guardar o histórico de funções dos
servidores no Interlegis
"""
servidor = models.ForeignKey(Servidor)
funcao = models.CharField(max_length=50)
cargo = models.CharField(max_length=50, blank=True, null=True)
inicio_funcao = models.DateField(u'início da função')
funcao = models.CharField(max_length=250, null=True)
cargo = models.CharField(max_length=250, null=True)
inicio_funcao = models.DateField(u'início da função', null=True)
fim_funcao = models.DateField(u'fim da função', blank=True, null=True)
descricao = models.TextField(u'descrição', blank=True, null=True)

16
sigi/apps/utils/admin_widgets.py

@ -0,0 +1,16 @@
from django.contrib.admin.widgets import AdminFileWidget
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
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(
u''' <a href="%s" target="_blank"><img src="%s" width="100"
height="100" alt="%s"/></a> <br/> %s''' % \
(image_url, image_url, file_name, _('Change:')))
output.append(super(AdminFileWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))

71
sigi/apps/utils/decorators.py

@ -0,0 +1,71 @@
# -*- coding: utf8 -*-
"""
Script baseado no arquivo decorators.py do django 1.3.
Ele foi copiado para usar o decorador ``login_required``
que possui o argumento ``login_url``, responsável por
redirecionar ao template de login desejado.
No ato de atualizar o framework, esse script torna-se
obsoleto.
"""
import urlparse
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.4 fallback.
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.utils.decorators import available_attrs
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
"""
def decorator(view_func):
@wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
path = request.build_absolute_uri()
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse.urlparse(login_url or
settings.LOGIN_URL)[:2]
current_scheme, current_netloc = urlparse.urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(path, login_url, redirect_field_name)
return _wrapped_view
return decorator
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
"""
Decorator for views that checks that the user is logged in, redirecting
to the log-in page if necessary.
"""
actual_decorator = user_passes_test(
lambda u: u.is_authenticated(),
login_url=login_url,
redirect_field_name=redirect_field_name
)
if function:
return actual_decorator(function)
return actual_decorator
def permission_required(perm, login_url=None):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
"""
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)

33
sigi/apps/utils/email.py

@ -0,0 +1,33 @@
# -*- coding: utf8 -*-
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
from django.conf import settings
def enviar_email(from_email, subject, template, tags):
"""Envia o email para o destinatário definido, a partir do template
definido para ser renderizado. Os argumentos são:
* from_email - Email do remetente
* subject - Assunto da Mensagem
* template - Template que será usado para gerar o corpo
da mensagem
* tags - Variáveis de contexto para ser renderizado no
template.
"""
if from_email is None:
raise ValueError("Insira o email do remetente.")
elif subject is None:
raise ValueError("Insira o assunto da mensagem.")
elif template is None:
raise ValueError(u"Template da mensagem não encontrado")
elif tags is None:
raise ValueError("Insira o conteúdo da mensagem.")
# Gerando a mensagem
mensagem = render_to_string(template, tags)
# Enviando a mensagem
email = EmailMessage(settings.EMAIL_SUBJECT_PREFIX + " " + subject, mensagem,
from_email, [from_email])
email.send()

42
sigi/apps/utils/validators.py

@ -0,0 +1,42 @@
# -*- coding: utf8 -*-
def valida_data(data_inicio, data_final):
"""Função responsável por validar se o intervalo das
datas estão erradas, ou seja, se a data de início está
maior ou igual a data final.
Caso seja maior ou igual retornará ``True``, caso contrário
retornará ``False``.
"""
if data_inicio >= data_final:
return True
else:
return False
def valida_periodo_data(di01, df01, di02, df02):
"""Função responsável por validar dois períodos de datas.
Isso é usado para verificar se determinado servidor exerceu
mais de uma função dentro de determinados períodos descritos
abaixo:
1 - A segunda função não pode ter exercido ao mesmo tempo que
a primeira função. Exemplo:
Primeiro Função: 01/05/2011 -- 01/11/2011
Segundo Função: 01/05/2011 -- 01/11/2011
2 - A segunda função não pode ter exercido, dentro do período
da primeira função. Exemplo:
Primeira Função: 01/05/2011 -- 01/11/2011
Segunda Função: 02/05/2011 -- 30/10/2011
"""
# Verificando a primeira situação
if di01 == di02 and df01 == df02:
return True
elif ((di01 >= di02) or (di02 <= df01)) and df01 <= df02:
return True
else:
return False

2
sigi/fixtures/treemenus/initial_data.json

File diff suppressed because one or more lines are too long

19
sigi/migrations/20111125_casas_funcionario.sql

@ -0,0 +1,19 @@
BEGIN;
-- retirando null de algumas colunas
ALTER TABLE "contatos_telefone" ALTER COLUMN nota DROP NOT NULL;
ALTER TABLE "contatos_telefone" ALTER COLUMN codigo_area DROP NOT NULL;
-- migrando dados de presidente da CasaLegislativa para Funcionarios
INSERT INTO casas_funcionario (casa_legislativa_id, cargo, setor, nome, nota, email)
SELECT id, 'Presidente', 'presidencia', presidente, '', '' FROM casas_casalegislativa;
-- migrando dados de telefones da CasaLegislativa para model generic Telefone
INSERT INTO contatos_telefone (numero, tipo, content_type_id, object_id, codigo_area, nota)
SELECT telefone, 'F', 12, id, '', '' FROM casas_casalegislativa;
-- deletando colunas fazer deois de estabilizar a versao
-- ALTER TABLE "casas_casalegislativa" DROP COLUMN presidente;
-- ALTER TABLE "casas_casalegislativa" DROP COLUMN telefone;
-- ALTER TABLE "contatos_telefone" DROP COLUMN codigo_area;
COMMIT;

7
sigi/migrations/20111125_diagnosticos_responsavel.sql

@ -0,0 +1,7 @@
BEGIN;
-- deletando colunas da CasaLegislativa
ALTER TABLE "diagnosticos_diagnostico" ADD COLUMN "responsavel_id" integer NOT NULL REFERENCES "servidores_servidor" ("id");
ALTER TABLE "diagnosticos_equipe" DROP COLUMN "is_chefe";
COMMIT;

23
sigi/settings.py

@ -29,6 +29,14 @@ DEFAULT_FROM_EMAIL = 'noreply@localhost'
INTERNAL_IPS = ('127.0.0.1',)
DEFAULT_FROM_EMAIL = 'sesostris@interlegis.leg.br'
EMAIL_SUBJECT_PREFIX = u'[SIGI]'
EMAIL_HOST = 'smtp.interlegis.leg.br'
EMAIL_PORT = 25
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
#EMAIL_USE_TLS = True
#DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = 'devel.db'
@ -53,10 +61,12 @@ ADMIN_MEDIA_PREFIX = '/sigi/admin_media/'
AUTH_LDAP_SERVER_URI = "ldap://w2k3dc01.interlegis.gov.br"
AUTH_LDAP_BIND_DN = u"cn=sigi-ldap,ou=Usuários de Sistema,ou=Usuários,ou=Interlegis,dc=interlegis,dc=gov,dc=br"
AUTH_LDAP_BIND_PASSWORD = "Sigi2609"
AUTH_LDAP_USER_SEARCH = LDAPSearch(u"ou=SINTER,ou=Usuários,ou=Sede,dc=interlegis,dc=gov,dc=br", ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)")
AUTH_LDAP_USER = u"ou=SINTER,ou=Usuários,ou=Sede,dc=interlegis,dc=gov,dc=br"
AUTH_LDAP_USER_SEARCH = LDAPSearch(AUTH_LDAP_USER, ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)")
# Set up the basic group parameters.
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=Grupos Organizacionais,ou=Sede,dc=interlegis,dc=gov,dc=br", ldap.SCOPE_SUBTREE, "(objectClass=Group)")
AUTH_LDAP_GROUP = "ou=Grupos Organizacionais,ou=Sede,dc=interlegis,dc=gov,dc=br"
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(AUTH_LDAP_GROUP, ldap.SCOPE_SUBTREE, "(objectClass=Group)")
AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn")
# Only users in this group can log in.
@ -74,9 +84,9 @@ AUTH_LDAP_USER_ATTR_MAP = {
}
# Populate the Django user_profile from the LDAP directory.
#AUTH_LDAP_PROFILE_ATTR_MAP = {
# "employee_number": "employeeNumber"
#}
AUTH_LDAP_PROFILE_ATTR_MAP = {
"nome_completo": "cn"
}
#AUTH_LDAP_PROFILE_FLAGS_BY_GROUP = {
# "is_awesome": "cn=awesome,ou=django,ou=groups,dc=example,dc=com",
@ -90,6 +100,8 @@ AUTH_LDAP_MIRROR_GROUPS = True
AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 3600
AUTH_PROFILE_MODULE = 'servidores.Servidor'
# Keep ModelBackend around for per-user permissions and maybe a local superuser.
AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
@ -116,6 +128,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.request',
'sigi.context_processors.charts_data',
)

10
sigi/sites.py

@ -24,8 +24,9 @@ from sigi.apps.mesas.admin import (Legislatura, LegislaturaAdmin, Coligacao,
from sigi.apps.parlamentares.admin import (Partido, PartidoAdmin, Parlamentar,
ParlamentarAdmin, Mandato, MandatoAdmin)
from sigi.apps.diagnosticos.admin import (Diagnostico, DiagnosticoAdmin, Pergunta,
BaseSchemaAdmin, Escolha, Anexo as AnexoDiagnostico,
AnexoAdmin as AnexoDiagnosticoAdmin)
PerguntaAdmin, Escolha, EscolhaAdmin, Anexo as AnexoDiagnostico,
AnexoAdmin as AnexoDiagnosticoAdmin, Categoria as
CategoriaDiagnostico)
from sigi.apps.servidores.admin import (Servidor, ServidorAdmin, Funcao, FuncaoAdmin,
Ferias, FeriasAdmin, Licenca, LicencaAdmin)
from sigi.apps.ocorrencias.admin import (Ocorrencia, OcorrenciaAdmin, Anexo as AnexoOcorrencia,
@ -93,9 +94,10 @@ default.register(Mandato, MandatoAdmin)
# sigi.apps.diagnosticos
default.register(Diagnostico, DiagnosticoAdmin)
default.register(Pergunta, BaseSchemaAdmin)
default.register(Escolha)
default.register(Pergunta, PerguntaAdmin)
default.register(Escolha, EscolhaAdmin)
default.register(AnexoDiagnostico, AnexoDiagnosticoAdmin)
default.register(CategoriaDiagnostico)
# sigi.apps.servidores
default.register(Servidor, ServidorAdmin)

6
sigi/templates/admin/diagnosticos/diagnostico/change_form.html

@ -0,0 +1,6 @@
{% extends 'admin/change_form.html' %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{{MEDIA_URL}}css/admin/diagnosticos/diagnostico/change_form.css">
{% endblock %}

27
sigi/templates/admin/servidores/servidor/change_form.html

@ -0,0 +1,27 @@
{% extends 'admin/change_form.html' %}
{% block form_top %}
{% if adminform.form.instance.user %}
<fieldset class="module aligned">
<h2>Dados do servidor LDAP</h2>
<div class="form-row">
<div>
<label>Primeiro Nome:</label>
<p>{{adminform.form.instance.user.first_name}}</p>
</div>
</div>
<div class="form-row">
<div>
<label>Último Nome:</label>
<p>{{adminform.form.instance.user.last_name}}</p>
</div>
</div>
<div class="form-row">
<div>
<label>Email Principal:</label>
<p>{{adminform.form.instance.user.email}}</p>
</div>
</div>
</fieldset>
{% endif %}
{% endblock %}

54
sigi/templates/base_mobile.html

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
{% block titulo %}
<title>SIGI - Diagnósticos</title>
{% endblock titulo %}
{% block media %}
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script language=javascript>
$(document).bind("mobileinit", function () {
$.mobile.ajaxEnabled = false;
$.mobile.loadingMessage = 'salvando';
});
</script>
<script src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
<script src="{{MEDIA_URL}}js/diagnosticos/diagnosticos_categorias_form.js"></script>
{% endblock media %}
</head>
<body>
<div data-role="page" id="page" class="type-interior">
<div data-role="header" data-position="fixed">
<img id="working" src="{{MEDIA_URL}}images/loader.gif" class="ui-btn-right"/>
{% block cabecalho %}{% endblock cabecalho %}
</div> <!-- header -->
<div data-role="content" class="content-primary">
{% block corpo %}{% endblock corpo %}
</div><!-- content -->
<div data-role="footer">
<div data-role="navbar">
<ul>
{% block rodape %}
<li><a href="{% url lista_diagnosticos %}" data-icon="home">Home</a></li>
{% endblock rodape %}
</ul>
</div>
</div> <!-- footer -->
</div> <!-- page -->
<a id='open-dialog' href="#dialog" data-rel="dialog" data-transition="pop" style='display:none;'></a>
<div data-role="page" id="dialog">
<div data-role="header">
<h1>Ops! Não foi possivel salvar os dados.</h1>
</div>
<div data-role="content" id="text">
Algum erro ocorreu ao salvar os dados do diagnóstico,
verifique a sua conectividade e/ou entre em contato
com a equipe técnica o mais rápido possível.
</div>
</div>
</body>
</html>

49
sigi/templates/diagnosticos/diagnosticos_categoria_casa_legislativa_form.html

@ -0,0 +1,49 @@
{% extends "base_mobile.html" %}
{% block media %}
{{ block.super }}
<link rel="stylesheet" href="{{MEDIA_URL}}css/diagnosticos/diagnosticos_categoria_model_form.css" />
{% endblock %}
{% block cabecalho %}
<h1>01. Identificação da Camara Municipal</h1>
<a href="{% url lista_categorias diagnostico.id %}?c=1" data-icon="arrow-l"
data-direction="reverse" data-theme="c" class="ui-btn-left">Voltar</a>
{% endblock cabecalho %}
{% block corpo %}
{% if form %}
<div id="waiting">
<!-- Exibe as perguntas apos o carregamento completo do javascript -->
<h2>Aguarde carregando perguntas para essa categoria...</h2>
</div>
<div id="form" style="display:none;">
<div data-role="fieldcontain">
<label class="attr">Município - UF</label>
{{ casa_legislativa.municipio }}
</div>
<div data-role="fieldcontain">
<label class="attr">Nome</label>
{{ casa_legislativa.nome }}
</div>
<form id="diagnostico" action="." method="post">
{% for field in form %}
<div data-role="fieldcontain">
<label class="attr" for="{{ field.name }}">{{ field.label }}</label>
{{ field }}
<span class="errors"></span>
</div>
{% endfor %}
</form>
</div>
{% else %}
<h2>Nenhuma existem perguntas para essa categoria.</h2>
{% endif %}
{% endblock corpo %}
{% block rodape %}
<ul>
<li><a href="{% url lista_categorias diagnostico.id %}" data-icon="grid" class="ui-state-persist ui-btn-active">Listar</a></li>
<li><a href="{% url lista_diagnosticos %}" data-icon="home">Home</a></li>
</ul>
{% endblock rodape %}

52
sigi/templates/diagnosticos/diagnosticos_categoria_contatos_form.html

@ -0,0 +1,52 @@
{% extends "base_mobile.html" %}
{% block media %}
{{ block.super }}
<link rel="stylesheet" href="{{MEDIA_URL}}css/diagnosticos/diagnosticos_categoria_model_form.css" />
{% endblock %}
{% block cabecalho %}
<h1>02. Identificação de Competências</h1>
<a href="{% url lista_categorias diagnostico.id %}?c=2" data-icon="arrow-l"
data-direction="reverse" data-theme="c" class="ui-btn-left">Voltar</a>
{% endblock cabecalho %}
{% block corpo %}
<div id="waiting">
<!-- Exibe as perguntas apos o carregamento completo do javascript -->
<h2>Aguarde carregando perguntas para essa categoria...</h2>
</div>
<div id="form" style="display:none;">
<form id="diagnostico" action="." method="post">
{% for form in forms %}
<h3>{{ form.instance.get_setor_display }}</h3>
{% for field in form %}
<div data-role="fieldcontain">
<label class="attr" for="{{ field.name }}">{{ field.label }}</label>
{{ field }}
<span class="errors"></span>
</div>
{% endfor %}
<h4>Telefones</h4>
{% for form in form.telefones.forms %}
<div class="phone" data-role="fieldcontain">
{{ form.tipo }} {{ form.numero}}
{% for field in form.hidden_fields %}
{{ field }}
<span class="errors"></span>
{% endfor %}
</div>
{% endfor %}
{{ form.telefones.management_form }}
<hr/>
{% endfor %}
</form>
</div>
{% endblock corpo %}
{% block rodape %}
<ul>
<li><a href="{% url lista_categorias diagnostico.id %}" data-icon="grid" class="ui-state-persist">Listar</a></li>
<li><a href="{% url lista_diagnosticos %}" data-icon="home">Home</a></li>
</ul>
{% endblock rodape %}

43
sigi/templates/diagnosticos/diagnosticos_categorias_form.html

@ -0,0 +1,43 @@
{% extends "base_mobile.html" %}
{% block media %}
{{ block.super }}
<link rel="stylesheet" href="{{MEDIA_URL}}css/diagnosticos/diagnosticos_categorias_form.css" />
{% endblock %}
{% block cabecalho %}
<h1>{{ categoria.nome }}</h1>
<a href="{% url lista_categorias diagnostico.id %}" data-icon="arrow-l"
data-direction="reverse" data-theme="c" class="ui-btn-left">Voltar</a>
{% endblock cabecalho %}
{% block corpo %}
{% if form %}
<div id="waiting">
<!-- Exibe as perguntas apos o carregamento completo do javascript -->
<h2>Aguarde carregando perguntas para essa categoria...</h2>
</div>
<div id="form" style="display:none;">
<form id="diagnostico" action="." method="post">
{% for field in form %}
<div data-role="fieldcontain" id="{{ field.name }}">
<label for="{{ field.name }}">{{ field.label }}</label>
{{ field }}
<span class="errors"></span>
</div>
{% endfor %}
</form>
</div>
{% else %}
<h2>Nenhuma existem perguntas para essa categoria.</h2>
{% endif %}
{% endblock corpo %}
<ul>
{% block rodape %}
{{ block.super }}
<li><a href="{% url lista_categorias diagnostico.id %}" data-icon="grid" class="ui-state-persist">Listar</a></li>
<li><a href="{% url logout %}" data-icon="delete">Sair</a></li>
{% endblock rodape %}
</ul>

63
sigi/templates/diagnosticos/diagnosticos_categorias_list.html

@ -0,0 +1,63 @@
{% extends "base_mobile.html" %}
{% block cabecalho %}
<h1>Categorias</h1>
<a href="{% url lista_diagnosticos %}" data-icon="arrow-l" data-direction="reverse" data-theme="c" class="ui-btn-left">Voltar</a>
{% endblock cabecalho %}
{% block media %}
{{ block.super }}
<link href="{{ MEDIA_URL }}css/diagnosticos/diagnosticos_categorias.css" rel="stylesheet" />
{% endblock media %}
{% block corpo %}
{% if categorias %}
<ul data-role="listview">
{% if ultima_categoria == 1 %}
<li class="ui-btn-last-c">
{% else %}
<li>
{% endif %}
<a href="{% url detalhes_categoria_casa_legislativa diagnostico.id %}">
<h4 class="ui-li-heading-read">01. Identificação da Casa Legislativa</h4>
</a>
</li>
{% if ultima_categoria == 2 %}
<li class="ui-btn-last-c">
{% else %}
<li>
{% endif %}
<a href="{% url detalhes_categoria_contatos diagnostico.id %}">
{% if diagnostico.contatos_respondidos %}
<h4 class="ui-li-heading-read">02. Identificação de Competências</h4>
{% else %}
<h4>02. Identificação de Competências</h4>
{% endif %}
</a>
</li>
{% for categoria in categorias %}
{% if ultima_categoria == categoria.id %}
<li class="ui-btn-last-c">
{% else %}
<li>
{% endif %}
<a href="{% url detalhes_categoria diagnostico.id categoria.id %}">
{% if categoria in diagnostico.categorias_respondidas %}
<h4 class="ui-li-heading-read">{{ categoria.nome }}</h4>
{% else %}
<h4>{{ categoria.nome }}</h4>
{% endif %}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<h2>Nenhuma categoria existente.</h2>
{% endif %}
{% endblock corpo %}
<ul>
{% block rodape %}
{{ block.super }}
<li><a href="{% url logout %}" data-icon="delete">Sair</a></li>
{% endblock rodape %}
</ul>

26
sigi/templates/diagnosticos/diagnosticos_list.html

@ -0,0 +1,26 @@
{% extends "base_mobile.html" %}
{% block cabecalho %}
<h1>Diagnósticos</h1>
<a href="{% url logout %}" data-icon="delete" class="ui-btn-left">Sair</a>
{% endblock cabecalho %}
{% block corpo %}
{% if diagnosticos %}
<ul data-role="listview">
{% for diagnostico in diagnosticos %}
<li>
<a href="{% url lista_categorias diagnostico.id %}">
<h4>{{ diagnostico.casa_legislativa.nome }}</h4>
<p><strong>Data:</strong> {{ diagnostico.data_visita_inicio|date:"d/m/Y" }} à {{ diagnostico.data_visita_fim|date:"d/m/Y" }} </p>
<p><strong>Responsável:</strong> {{ diagnostico.responsavel.nome_completo }}</p>
</a>
</li>
{% endfor %}
</ul>
{% else %}
<h2>Nenhum diagnóstico aberto.</h2>
{% endif %}
{% endblock corpo %}
{% block rodape %}{% endblock rodape %}

51
sigi/templates/diagnosticos/diagnosticos_login.html

@ -0,0 +1,51 @@
<!DOCTYPE HTML>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SIGI - Login de Acesso</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script language=javascript>
$(document).bind("mobileinit", function () {
$.mobile.ajaxEnabled = false;
$.mobile.loadingMessage = 'salvando';
});
</script>
<script src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
</head>
<body>
<div data-role="header" data-theme="d">
<img src="{{ MEDIA_URL }}images/mobile/logointerlegis_mobile.png" alt="Interlegis"/>
</div> <!-- header -->
{% if form.errors %}
<div data-role="header" data-theme="b">
<h2>Erro ao logar</h2>
<ul>
{% for field in form %}
{% if field.errors %}
<li>{{ field.errors|striptags }}</li>
{% endif %}
{% endfor %}
</ul>
<p>Verifique se seu login e senha foram preenchidos corretamente.</p>
</div> <!-- error messages -->
{% endif %}
<div data-role="content">
<form action="{% url django.contrib.auth.views.login %}" method="POST">
{% csrf_token %}
<div data-role="fieldcontain" class="ui-hide-label ui-body">
<label for="username">Usuário:</label>
<input type="text" name="username" id="id_username" placeholder="Usuário">
</div>
<div data-role="fieldcontain" class="ui-hide-label">
<label for="password">Senha de Acesso:</label>
<input type="password" name="password" id="id_password" placeholder="Senha de Acesso">
</div>
<input type="hidden" name="next" value="{% url lista_diagnosticos %}" />
<button type="submit">Entrar</button>
</form>
</div>
</body>
</html>

8
sigi/templates/diagnosticos/email_diagnostico_atualizado.txt

@ -0,0 +1,8 @@
Foi atualizado um diagnóstico no SIGI. Os dados do diagnóstico são:
Criador do Diagnóstico: {{ servidor }}
Dados do Diagnóstico: {{ casa_legislativa }}
Data do Diagnóstico: {{ data_diagnostico }}
Data do Relatório do Diagnóstico: {{ data_relatorio_diagnostico }}
O seu status está como {{ status }}

10
sigi/templates/diagnosticos/email_diagnostico_publicado.txt

@ -0,0 +1,10 @@
Olá {{ responsavel }}
Foi publicado um diagnóstico no SIGI.
Os dados do diagnóstico são:
Dados do Diagnóstico: {{ casa_legislativa }}
Data do Diagnóstico: {{ data_diagnostico }}
Para mais detalhes, acesse:
http://{{ host }}{{ url_diagnostico }}

15
sigi/templates/mobile/404.html

@ -0,0 +1,15 @@
{% extends "base_mobile.html" %}
{% block cabecalho %}
<h1>Ocorreu um erro</h1>
{% endblock cabecalho %}
{% block corpo %}
<p>A página que está procurando não existe. </p>
<p>Verifique se o diagnóstico, categoria ou pergunta está cadastrado no sistema.</p>
<a href="{% url lista_diagnosticos %}" data-icon="arrow-l"
data-direction="reverse" data-role="button" data-theme="c" class="ui-btn-left">Voltar</a>
{% endblock corpo %}
{% block rodape %}
{% endblock rodape %}

74
sigi/urls.py

@ -1,6 +1,7 @@
#-*- coding:utf-8 -*-
from django.conf import settings
from django.conf.urls.defaults import *
from django.conf.urls.defaults import patterns, include, url
from django.views.generic.simple import redirect_to
import sites
# register admin filters
@ -9,67 +10,72 @@ import admin.filterspecs
urlpatterns = patterns(
'',
('^$', redirect_to, {'url': '/sigi/'}),
# Diagnosticos
(r'^sigi/mobile/diagnosticos/', include('sigi.apps.diagnosticos.urls')),
# Informacoes de uma casa legislativa
(r'^casas/casalegislativa/(?P<id>\w+)/report_complete/',
(r'^sigi/casas/casalegislativa/(?P<id>\w+)/report_complete/',
'sigi.apps.casas.views.report_complete'),
(r'^casas/casalegislativa/report_complete/',
(r'^sigi/casas/casalegislativa/report_complete/',
'sigi.apps.casas.views.report_complete'),
# reports labels
(r'^casas/casalegislativa/labels/',
(r'^sigi/casas/casalegislativa/labels/',
'sigi.apps.casas.views.labels_report'),
(r'^casas/casalegislativa/(?P<id>\w+)/labels/',
(r'^sigi/casas/casalegislativa/(?P<id>\w+)/labels/',
'sigi.apps.casas.views.labels_report'),
# reports labels sem presidente
(r'^casas/casalegislativa/labels_sem_presidente/',
# reports labels sem presidente
(r'^sigi/casas/casalegislativa/labels_sem_presidente/',
'sigi.apps.casas.views.labels_report_sem_presidente'),
(r'^casas/casalegislativa/(?P<id>\w+)/labels_sem_presidente/',
(r'^sigi/casas/casalegislativa/(?P<id>\w+)/labels_sem_presidente/',
'sigi.apps.casas.views.labels_report_sem_presidente'),
# reports casa
(r'^casas/casalegislativa/reports/',
(r'^sigi/casas/casalegislativa/reports/',
'sigi.apps.casas.views.report'),
(r'^casas/casalegislativa/casas_sem_convenio_report/',
(r'^sigi/casas/casalegislativa/casas_sem_convenio_report/',
'sigi.apps.casas.views.casas_sem_convenio_report'),
# reports convenios
(r'^convenios/convenio/reports/',
(r'^sigi/convenios/convenio/reports/',
'sigi.apps.convenios.views.report'),
#Carrinho Casa
(r'^casas/casalegislativa/carrinho/deleta_itens_carrinho',
#Carrinho Casa
(r'^sigi/casas/casalegislativa/carrinho/deleta_itens_carrinho',
'sigi.apps.casas.views.deleta_itens_carrinho'),
(r'^casas/casalegislativa/carrinho/excluir_carrinho',
(r'^sigi/casas/casalegislativa/carrinho/excluir_carrinho',
'sigi.apps.casas.views.excluir_carrinho'),
(r'^casas/casalegislativa/carrinho/',
(r'^sigi/casas/casalegislativa/carrinho/',
'sigi.apps.casas.views.visualizar_carrinho'),
#Carrinho Convenio
(r'^convenios/convenio/carrinho/deleta_itens_carrinho',
#Carrinho Convenio
(r'^sigi/convenios/convenio/carrinho/deleta_itens_carrinho',
'sigi.apps.convenios.views.deleta_itens_carrinho'),
(r'^convenios/convenio/carrinho/excluir_carrinho',
(r'^sigi/convenios/convenio/carrinho/excluir_carrinho',
'sigi.apps.convenios.views.excluir_carrinho'),
(r'^convenios/convenio/carrinho/',
(r'^sigi/convenios/convenio/carrinho/',
'sigi.apps.convenios.views.visualizar_carrinho'),
#CSV Casa
(r'^casas/casalegislativa/csv/',
'sigi.apps.casas.views.export_csv'),
#CSV Casa
(r'^sigi/casas/casalegislativa/csv/',
'sigi.apps.casas.views.export_csv'),
#CSV Convenio
(r'^convenios/convenio/csv/',
(r'^sigi/convenios/convenio/csv/',
'sigi.apps.convenios.views.export_csv'),
# Resumo por região PDF
(r'^reportsRegiao/(?P<regiao>\w+)',
# Resumo por região PDF
(r'^sigi/reportsRegiao/(?P<regiao>\w+)',
'sigi.apps.convenios.views.report_regiao'),
# Submenu com Birt reports
(r'^birt/menu/(?P<folder>\w+)',
# Submenu com Birt reports
(r'^sigi/birt/menu/(?P<folder>\w+)',
'sigi.apps.birt.views.menu'),
# Execução com Birt reports
(r'^birt/run/(?P<file>.+)',
# Execução com Birt reports
(r'^sigi/birt/run/(?P<file>.+)',
'sigi.apps.birt.views.run'),
# Mostrar um relatório em formato HTML
(r'^birt/showreport/',
# Mostrar um relatório em formato HTML
(r'^sigi/birt/showreport/',
'sigi.apps.birt.views.show'),
# Menu com Birt reports
(r'^birt/',
# Menu com Birt reports
(r'^sigi/birt/',
'sigi.apps.birt.views.menu'),
# automatic interface based on admin
(r'^(.*)', sites.default.root),
(r'^sigi/(.*)', sites.default.root),
)
if settings.DEBUG:

Loading…
Cancel
Save