Browse Source

Merge branch 'master' into 758-refatora-painel

pull/792/head
Eduardo Calil 9 years ago
parent
commit
d87e0fed85
  1. 15
      README.rst
  2. 129
      sapl/compilacao/models.py
  3. 14
      sapl/compilacao/utils.py
  4. 83
      sapl/compilacao/views.py
  5. 21
      sapl/legacy_migration_settings.py
  6. 16
      sapl/materia/forms.py
  7. 2
      sapl/templates/sessao/adicionar_varias_materias_expediente.html
  8. 1
      sapl/urls.py

15
README.rst

@ -190,24 +190,20 @@ Instalação e configuração das dependências do projeto
http://localhost:8000/ http://localhost:8000/
Instruções para criação dos grupos de perfis de usuários e os usuários de testes Instruções para criação do super usuário e de usuários de testes
=========================================================================== ===========================================================================
* Criar super usuário do django-contrib-admin (Será solicitado alguns dados para criação):: * Criar super usuário do django-contrib-admin (Será solicitado alguns dados para criação)::
./manage.py createsuperuser ./manage.py createsuperuser
Os perfis semânticos do SAPL devem ser criados manualmente através da execução de um script que gera esses perfis e adiciona um usuário padrão em cada perfil. Para testar o comportamento de cada perfil é necessário executar este script: * `Os perfis semânticos do SAPL <https://github.com/interlegis/sapl/blob/master/sapl/rules/__init__.py>`_ são fixos e atualizados a cada execução do comando:
* Execute:: ./manage.py migrate
./manage.py shell_plus
* Será aberto um prompt do python customizado com diversas funcionalidades do django e do sapl. Execute dentro do prompt::
%run scripts/inicializa_grupos_autorizacoes.py * Os perfis fixos não aceitam customização via admin, porém outros grupos podem ser criados. O SAPL não interferirá no conjunto de permissões definidas em grupos customizados e se comportará diante de usuários segundo seus grupos e suas permissões.
* Os usuários criados, todos com senha "interlegis", serão:: * Os usuários de testes de perfil são criados apenas se o SAPL estiver rodando em modo DEBUG=True. Todos com senha "interlegis", serão::
operador_administrativo operador_administrativo
operador_protocoloadm operador_protocoloadm
@ -217,7 +213,6 @@ Os perfis semânticos do SAPL devem ser criados manualmente através da execuç
operador_sessao operador_sessao
operador_painel operador_painel
operador_geral operador_geral
operador_autor
Instruções para Tradução Instruções para Tradução
======================== ========================

129
sapl/compilacao/models.py

@ -1,3 +1,5 @@
from datetime import datetime
from django.contrib import messages from django.contrib import messages
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -6,9 +8,11 @@ from django.db.models import F, Q
from django.db.models.aggregates import Max from django.db.models.aggregates import Max
from django.http.response import Http404 from django.http.response import Http404
from django.template import defaultfilters from django.template import defaultfilters
from django.utils.decorators import classonlymethod
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from sapl.compilacao.utils import int_to_letter, int_to_roman from sapl.compilacao.utils import int_to_letter, int_to_roman,\
get_integrations_view_names
from sapl.utils import YES_NO_CHOICES, get_settings_auth_user_model from sapl.utils import YES_NO_CHOICES, get_settings_auth_user_model
@ -280,6 +284,129 @@ class TextoArticulado(TimestampedMixin):
return True return True
@classonlymethod
def update_or_create(cls, view_integracao, obj):
map_fields = view_integracao.map_fields
ta_values = getattr(view_integracao, 'ta_values', {})
related_object_type = ContentType.objects.get_for_model(obj)
ta = TextoArticulado.objects.filter(
object_id=obj.pk,
content_type=related_object_type)
ta_exists = bool(ta.exists())
if not ta_exists:
tipo_ta = TipoTextoArticulado.objects.filter(
content_type=related_object_type).first()
ta = TextoArticulado()
ta.tipo_ta = tipo_ta
ta.content_object = obj
ta.privacidade = ta_values.get('privacidade', STATUS_TA_EDITION)
ta.editing_locked = ta_values.get('editing_locked', False)
ta.editable_only_by_owners = ta_values.get(
'editable_only_by_owners', False)
else:
ta = ta[0]
if not ta.data:
ta.data = getattr(obj, map_fields['data']
if map_fields['data'] else 'xxx',
datetime.now())
if not ta.data:
ta.data = datetime.now()
ta.ementa = getattr(
obj, map_fields['ementa']
if map_fields['ementa'] else 'xxx', _(
'Integração com %s sem ementa.') % obj)
ta.observacao = getattr(
obj, map_fields['observacao']
if map_fields['observacao'] else 'xxx', '')
ta.numero = getattr(
obj, map_fields['numero']
if map_fields['numero'] else 'xxx', int('%s%s%s' % (
int(datetime.now().year),
int(datetime.now().month),
int(datetime.now().day))))
ta.ano = getattr(obj, map_fields['ano']
if map_fields['ano'] else 'xxx', datetime.now().year)
ta.save()
return ta
def clone_for(self, obj):
# O clone gera um texto válido original dada a base self,
# mesmo sendo esta base um texto compilado.
# Os dispositivos a clonar será com base no texto compilado
assert self.tipo_ta and self.tipo_ta.content_type, _(
'Não é permitido chamar o método clone_for '
'para Textos Articulados independentes.')
view_integracao = list(filter(lambda x:
x.model == obj._meta.model,
get_integrations_view_names()))
assert len(view_integracao) > 0, _(
'Não é permitido chamar o método clone_for '
'se não existe integração.')
assert len(view_integracao) == 1, _(
'Não é permitido haver mais de uma integração para um Model.')
view_integracao = view_integracao[0]
ta = TextoArticulado.update_or_create(view_integracao, obj)
dispositivos = Dispositivo.objects.filter(ta=self).order_by('ordem')
map_ids = {}
for d in dispositivos:
id_old = d.id
# TODO
# validar isso: é o suficiente para pegar apenas o texto válido?
# exemplo:
# quando uma matéria for alterada por uma emenda
# ao usar esta função para gerar uma norma deve vir apenas
# o texto válido, compilado...
if d.dispositivo_subsequente:
continue
d.id = None
d.inicio_vigencia = ta.data
d.fim_vigencia = None
d.inicio_eficacia = ta.data
d.fim_eficacia = None
d.publicacao = None
d.ta = ta
d.ta_publicado = None
d.dispositivo_subsequente = None
d.dispositivo_substituido = None
d.dispositivo_vigencia = None
d.dispositivo_atualizador = None
d.save()
map_ids[id_old] = d.id
dispositivos = Dispositivo.objects.filter(ta=ta).order_by('ordem')
for d in dispositivos:
if not d.dispositivo_pai:
continue
d.dispositivo_pai_id = map_ids[d.dispositivo_pai_id]
d.save()
return ta
def reagrupar_ordem_de_dispositivos(self): def reagrupar_ordem_de_dispositivos(self):
dpts = Dispositivo.objects.filter(ta=self) dpts = Dispositivo.objects.filter(ta=self)

14
sapl/compilacao/utils.py

@ -1,3 +1,4 @@
import sys
DISPOSITIVO_SELECT_RELATED = ( DISPOSITIVO_SELECT_RELATED = (
'tipo_dispositivo', 'tipo_dispositivo',
@ -52,3 +53,16 @@ def int_to_letter(int_value):
result = chr(rest + 65) + result result = chr(rest + 65) + result
result = chr(int_value + 65) + result result = chr(int_value + 65) + result
return result return result
def get_integrations_view_names():
result = []
modules = sys.modules
for key, value in modules.items():
if key.endswith('.views'):
for v in value.__dict__.values():
if hasattr(v, '__bases__'):
for base in v.__bases__:
if 'IntegracaoTaView' in str(base):
result.append(v)
return result

83
sapl/compilacao/views.py

@ -45,7 +45,8 @@ from sapl.compilacao.models import (Dispositivo, Nota,
VeiculoPublicacao, Vide, STATUS_TA_EDITION, VeiculoPublicacao, Vide, STATUS_TA_EDITION,
STATUS_TA_PRIVATE, STATUS_TA_PUBLIC) STATUS_TA_PRIVATE, STATUS_TA_PUBLIC)
from sapl.compilacao.utils import (DISPOSITIVO_SELECT_RELATED, from sapl.compilacao.utils import (DISPOSITIVO_SELECT_RELATED,
DISPOSITIVO_SELECT_RELATED_EDIT) DISPOSITIVO_SELECT_RELATED_EDIT,
get_integrations_view_names)
from sapl.crud.base import Crud, CrudListView, make_pagination from sapl.crud.base import Crud, CrudListView, make_pagination
from sapl.settings import BASE_DIR from sapl.settings import BASE_DIR
@ -60,19 +61,6 @@ TipoDispositivoCrud = Crud.build(
logger = logging.getLogger(BASE_DIR.name) logger = logging.getLogger(BASE_DIR.name)
def get_integrations_view_names():
result = []
modules = sys.modules
for key, value in modules.items():
if key.endswith('.views'):
for v in value.__dict__.values():
if hasattr(v, '__bases__'):
for base in v.__bases__:
if base == IntegracaoTaView:
result.append(v)
return result
def choice_models_in_extenal_views(): def choice_models_in_extenal_views():
integrations_view_names = get_integrations_view_names() integrations_view_names = get_integrations_view_names()
result = [(None, '-------------'), ] result = [(None, '-------------'), ]
@ -165,54 +153,35 @@ class IntegracaoTaView(TemplateView):
object_id=item.pk, object_id=item.pk,
content_type=related_object_type) content_type=related_object_type)
tipo_ta = TipoTextoArticulado.objects.filter(
content_type=related_object_type)
ta_exists = bool(ta.exists()) ta_exists = bool(ta.exists())
if not ta_exists: if (ta_exists or
ta = TextoArticulado() (request.user.has_perm(
tipo_ta = TipoTextoArticulado.objects.filter( 'compilacao.change_dispositivo_edicao_dinamica') and
content_type=related_object_type)[:1] ta_values.get('privacidade', STATUS_TA_EDITION
if tipo_ta.exists(): ) != STATUS_TA_PRIVATE) or
ta.tipo_ta = tipo_ta[0] (request.user.has_perm(
ta.content_object = item 'compilacao.change_your_dispositivo_edicao_dinamica') and
ta_values.get('privacidade', STATUS_TA_EDITION
ta.privacidade = ta_values.get('privacidade', STATUS_TA_EDITION) ) == STATUS_TA_PUBLIC)):
"""
o texto articulado será criado/atualizado se:
- texto articulado foi criado.
ta.editing_locked = ta_values.get('editing_locked', False) - não foi criado e o usuário possui permissão para criar
ta.editable_only_by_owners = ta_values.get( desde que o texto não seja um texto privado pois a permissão
'editable_only_by_owners', False) para criar textos privados é diferente.
- não foi criado e o usuário possui permissão para criar desde
que o texto seja privado e a permissão seja específica para
textos privados.
"""
pass
else: else:
ta = ta[0] messages.info(request, _('%s não possui %s.') % (
item, TextoArticulado._meta.verbose_name))
if not ta.data: return redirect('/message')
ta.data = getattr(item, map_fields['data']
if map_fields['data'] else 'xxx',
datetime.now())
if not ta.data:
ta.data = datetime.now()
ta.ementa = getattr(
item, map_fields['ementa']
if map_fields['ementa'] else 'xxx', _(
'Integração com %s sem ementa.') % item)
ta.observacao = getattr(
item, map_fields['observacao']
if map_fields['observacao'] else 'xxx', '')
ta.numero = getattr(
item, map_fields['numero']
if map_fields['numero'] else 'xxx', int('%s%s%s' % (
int(datetime.now().year),
int(datetime.now().month),
int(datetime.now().day))))
ta.ano = getattr(item, map_fields['ano']
if map_fields['ano'] else 'xxx', datetime.now().year)
ta.save() ta = TextoArticulado.update_or_create(self, item)
if not ta_exists: if not ta_exists:
if ta.editable_only_by_owners and\ if ta.editable_only_by_owners and\

21
sapl/legacy_migration_settings.py

@ -1,19 +1,30 @@
# Settings for data migration from mysql legacy to new postgres database import os
from decouple import Config, RepositoryEnv, AutoConfig
from dj_database_url import parse as db_url
from .settings import * # flake8: noqa from .settings import * # flake8: noqa
config = AutoConfig()
config.config = Config(RepositoryEnv(os.path.abspath('sapl/legacy/.env')))
INSTALLED_APPS += ( INSTALLED_APPS += (
'sapl.legacy', # legacy reversed model definitions 'sapl.legacy', # legacy reversed model definitions
) )
DATABASES['legacy'] = { DATABASES['legacy'] = config('DATABASE_URL', cast=db_url,)
"""DATABASES['legacy'] = {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': 'sapl25', 'NAME': 'legacy_interlegis',
'USER': 'root', 'USER': 'root',
'PASSWORD': 'admin', 'PASSWORD': '',
'HOST': 'localhost', # Or an IP Address that your DB is hosted on 'HOST': '', # Or an IP Address that your DB is hosted on
'PORT': '3306', 'PORT': '3306',
} }
"""
DATABASE_ROUTERS = ['sapl.legacy.router.LegacyRouter', ] DATABASE_ROUTERS = ['sapl.legacy.router.LegacyRouter', ]

16
sapl/materia/forms.py

@ -22,7 +22,7 @@ import django_filters
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.comissoes.models import Comissao from sapl.comissoes.models import Comissao
from sapl.compilacao.models import STATUS_TA_PRIVATE,\ from sapl.compilacao.models import STATUS_TA_PRIVATE,\
STATUS_TA_IMMUTABLE_PUBLIC, TextoArticulado STATUS_TA_IMMUTABLE_PUBLIC, TextoArticulado, STATUS_TA_PUBLIC
from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column,
to_row) to_row)
from sapl.materia.models import TipoProposicao, MateriaLegislativa,\ from sapl.materia.models import TipoProposicao, MateriaLegislativa,\
@ -472,7 +472,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
ementa = django_filters.CharFilter(lookup_expr='icontains') ementa = django_filters.CharFilter(lookup_expr='icontains')
em_tramitacao = django_filters.ChoiceFilter(required=False, em_tramitacao = django_filters.ChoiceFilter(required=False,
label=u'Ano da Matéria', label=u'Em tramitação',
choices=em_tramitacao) choices=em_tramitacao)
o = MateriaPesquisaOrderingFilter() o = MateriaPesquisaOrderingFilter()
@ -1212,14 +1212,10 @@ class ConfirmarProposicaoForm(ProposicaoForm):
if proposicao.texto_articulado.exists(): if proposicao.texto_articulado.exists():
ta = proposicao.texto_articulado.first() ta = proposicao.texto_articulado.first()
ta_materia = ta.clone_for(materia)
ta.id = None ta_materia.editing_locked = True
ta.content_object = materia ta_materia.privacidade = STATUS_TA_IMMUTABLE_PUBLIC
ta.save() ta_materia.save()
pass
# FIXME - gerar texto_articulado da materia com base na prop.
# materia.texto_articulo = proposicao.texto_articulado
self.instance.results['messages']['success'].append(_( self.instance.results['messages']['success'].append(_(
'Matéria Legislativa registrada com sucesso (%s)' 'Matéria Legislativa registrada com sucesso (%s)'

2
sapl/templates/sessao/adicionar_varias_materias_expediente.html

@ -52,7 +52,7 @@
{% for m in page_obj %} {% for m in page_obj %}
<tr> <tr>
<td> <td>
<input type="checkbox" name="materia_id" value="{{m.id}}" {% if check %} checked {% endif %}/> <input type="checkbox" name="materia_id" id="{{m.id}}" value="{{m.id}}" {% if check %} checked {% endif %}>
<strong><a href="{% url 'sapl.materia:materialegislativa_detail' m.id %}">{{m.tipo.sigla}} {{m.numero}}/{{m.ano}} - {{m.tipo}}</strong></a></br> <strong><a href="{% url 'sapl.materia:materialegislativa_detail' m.id %}">{{m.tipo.sigla}} {{m.numero}}/{{m.ano}} - {{m.tipo}}</strong></a></br>
<strong>Autores:</strong> <strong>Autores:</strong>
{% for a in m.autoria_set.all %} {% for a in m.autoria_set.all %}

1
sapl/urls.py

@ -36,6 +36,7 @@ import sapl.sessao.urls
urlpatterns = [ urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='index.html')), url(r'^$', TemplateView.as_view(template_name='index.html')),
url(r'^message$', TemplateView.as_view(template_name='base.html')),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'', include(sapl.comissoes.urls)), url(r'', include(sapl.comissoes.urls)),

Loading…
Cancel
Save