Browse Source

Merge branch 'migracao' into 3.1.x

pull/2023/head
Marcio Mazza 7 years ago
parent
commit
ebf571c40a
  1. 7
      sapl/comissoes/legacy.yaml
  2. 25
      sapl/comissoes/migrations/0015_auto_20180613_2023.py
  3. 21
      sapl/comissoes/migrations/0016_auto_20180613_2121.py
  4. 9
      sapl/comissoes/models.py
  5. 26
      sapl/comissoes/views.py
  6. 2
      sapl/legacy/management/commands/migracao_25_31.py
  7. 6
      sapl/legacy/migracao.py
  8. 70
      sapl/legacy/migracao_dados.py
  9. 36
      sapl/legacy/migracao_documentos.py
  10. 11
      sapl/legacy/migracao_usuarios.py
  11. 14
      sapl/legacy/models.py
  12. 85
      sapl/legacy/scripts/exporta_zope/exporta_zope.py
  13. 12
      sapl/sessao/legacy.yaml

7
sapl/comissoes/legacy.yaml

@ -42,3 +42,10 @@ Participacao (ComposicaoComissao):
observacao: obs_composicao observacao: obs_composicao
parlamentar: cod_parlamentar parlamentar: cod_parlamentar
titular: ind_titular titular: ind_titular
Reuniao (ReuniaoComissao):
comissao: cod_comissao
numero: num_reuniao
data: dat_inicio_reuniao
observacao: txt_observacao

25
sapl/comissoes/migrations/0015_auto_20180613_2023.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-06-13 23:23
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('comissoes', '0014_auto_20180503_1055'),
]
operations = [
migrations.AlterField(
model_name='reuniao',
name='hora_fim',
field=models.TimeField(null=True, verbose_name='Horário de Término (hh:mm)'),
),
migrations.AlterField(
model_name='reuniao',
name='hora_inicio',
field=models.TimeField(null=True, verbose_name='Horário de Início (hh:mm)'),
),
]

21
sapl/comissoes/migrations/0016_auto_20180613_2121.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-06-14 00:21
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('comissoes', '0015_auto_20180613_2023'),
]
operations = [
migrations.AlterField(
model_name='reuniao',
name='periodo',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='comissoes.Periodo', verbose_name='Periodo da Composicão da Comissão'),
),
]

9
sapl/comissoes/models.py

@ -184,13 +184,16 @@ class Participacao(models.Model): # ComposicaoComissao
def get_comissao_media_path(instance, subpath, filename): def get_comissao_media_path(instance, subpath, filename):
return './sapl/comissao/%s/%s/%s' % (instance.numero, subpath, filename) return './sapl/comissao/%s/%s/%s' % (instance.numero, subpath, filename)
def pauta_upload_path(instance, filename): def pauta_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='pauta', pk_first=True) return texto_upload_path(instance, filename, subpath='pauta', pk_first=True)
def ata_upload_path(instance, filename): def ata_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='ata', pk_first=True) return texto_upload_path(instance, filename, subpath='ata', pk_first=True)
def anexo_upload_path(instance, filename): def anexo_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='anexo', pk_first=True) return texto_upload_path(instance, filename, subpath='anexo', pk_first=True)
@ -198,6 +201,7 @@ def anexo_upload_path(instance, filename):
class Reuniao(models.Model): class Reuniao(models.Model):
periodo = models. ForeignKey( periodo = models. ForeignKey(
Periodo, Periodo,
null=True,
on_delete=models.PROTECT, on_delete=models.PROTECT,
verbose_name=_('Periodo da Composicão da Comissão')) verbose_name=_('Periodo da Composicão da Comissão'))
comissao = models.ForeignKey( comissao = models.ForeignKey(
@ -211,8 +215,10 @@ class Reuniao(models.Model):
max_length=150, blank=True, verbose_name=_('Tema da Reunião')) max_length=150, blank=True, verbose_name=_('Tema da Reunião'))
data = models.DateField(verbose_name=_('Data')) data = models.DateField(verbose_name=_('Data'))
hora_inicio = models.TimeField( hora_inicio = models.TimeField(
null=True,
verbose_name=_('Horário de Início (hh:mm)')) verbose_name=_('Horário de Início (hh:mm)'))
hora_fim = models.TimeField( hora_fim = models.TimeField(
null=True,
verbose_name=_('Horário de Término (hh:mm)')) verbose_name=_('Horário de Término (hh:mm)'))
local_reuniao = models.CharField( local_reuniao = models.CharField(
max_length=100, blank=True, verbose_name=_('Local da Reunião')) max_length=100, blank=True, verbose_name=_('Local da Reunião'))
@ -292,7 +298,8 @@ class DocumentoAcessorio(models.Model):
on_delete=models.PROTECT) on_delete=models.PROTECT)
nome = models.CharField(max_length=50, verbose_name=_('Nome')) nome = models.CharField(max_length=50, verbose_name=_('Nome'))
data = models.DateField(blank=True, null=True, default=None, verbose_name=_('Data')) data = models.DateField(blank=True, null=True,
default=None, verbose_name=_('Data'))
autor = models.CharField( autor = models.CharField(
max_length=100, verbose_name=_('Autor')) max_length=100, verbose_name=_('Autor'))
ementa = models.TextField(blank=True, verbose_name=_('Ementa')) ementa = models.TextField(blank=True, verbose_name=_('Ementa'))

26
sapl/comissoes/views.py

@ -8,20 +8,20 @@ from django.views.generic.base import RedirectView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
from sapl.base.models import AppConfig as AppsAppConfig from sapl.base.models import AppConfig as AppsAppConfig
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, from sapl.comissoes.apps import AppConfig
CrudAux, MasterDetailCrud, from sapl.comissoes.forms import (ComissaoForm, ComposicaoForm,
DocumentoAcessorioCreateForm,
DocumentoAcessorioEditForm,
ParticipacaoCreateForm, ParticipacaoEditForm,
PeriodoForm, ReuniaoForm)
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
MasterDetailCrud,
PermissionRequiredForAppCrudMixin) PermissionRequiredForAppCrudMixin)
from sapl.comissoes.forms import (ComissaoForm, ComposicaoForm, DocumentoAcessorioCreateForm,
DocumentoAcessorioEditForm, ParticipacaoCreateForm,
ParticipacaoEditForm, ReuniaoForm, PeriodoForm)
from sapl.materia.models import MateriaLegislativa, Tramitacao from sapl.materia.models import MateriaLegislativa, Tramitacao
from .models import (CargoComissao, Comissao, Composicao, DocumentoAcessorio, from .models import (CargoComissao, Comissao, Composicao, DocumentoAcessorio,
Participacao, Periodo, TipoComissao, Reuniao) Participacao, Periodo, Reuniao, TipoComissao)
from sapl.comissoes.apps import AppConfig
def pegar_url_composicao(pk): def pegar_url_composicao(pk):
@ -30,6 +30,7 @@ def pegar_url_composicao(pk):
url = reverse('sapl.comissoes:composicao_detail', kwargs={'pk': comp_pk}) url = reverse('sapl.comissoes:composicao_detail', kwargs={'pk': comp_pk})
return url return url
def pegar_url_reuniao(pk): def pegar_url_reuniao(pk):
documentoacessorio = DocumentoAcessorio.objects.get(id=pk) documentoacessorio = DocumentoAcessorio.objects.get(id=pk)
r_pk = documentoacessorio.reuniao.pk r_pk = documentoacessorio.reuniao.pk
@ -42,6 +43,7 @@ TipoComissaoCrud = CrudAux.build(
TipoComissao, 'tipo_comissao', list_field_names=[ TipoComissao, 'tipo_comissao', list_field_names=[
'sigla', 'nome', 'natureza', 'dispositivo_regimental']) 'sigla', 'nome', 'natureza', 'dispositivo_regimental'])
class PeriodoComposicaoCrud(CrudAux): class PeriodoComposicaoCrud(CrudAux):
model = Periodo model = Periodo
@ -77,6 +79,7 @@ class ParticipacaoCrud(MasterDetailCrud):
form_class = ParticipacaoEditForm form_class = ParticipacaoEditForm
class DeleteView(MasterDetailCrud.DeleteView): class DeleteView(MasterDetailCrud.DeleteView):
def get_success_url(self): def get_success_url(self):
composicao_comissao_pk = self.object.composicao.comissao.pk composicao_comissao_pk = self.object.composicao.comissao.pk
composicao_pk = self.object.composicao.pk composicao_pk = self.object.composicao.pk
@ -98,7 +101,6 @@ class ComposicaoCrud(MasterDetailCrud):
comissao = Comissao.objects.get(id=self.kwargs['pk']) comissao = Comissao.objects.get(id=self.kwargs['pk'])
return {'comissao': comissao} return {'comissao': comissao}
class ListView(MasterDetailCrud.ListView): class ListView(MasterDetailCrud.ListView):
template_name = "comissoes/composicao_list.html" template_name = "comissoes/composicao_list.html"
paginate_by = None paginate_by = None
@ -180,6 +182,7 @@ class MateriasTramitacaoListView(ListView):
context['object'] = Comissao.objects.get(id=self.kwargs['pk']) context['object'] = Comissao.objects.get(id=self.kwargs['pk'])
return context return context
class ReuniaoCrud(MasterDetailCrud): class ReuniaoCrud(MasterDetailCrud):
model = Reuniao model = Reuniao
parent_field = 'comissao' parent_field = 'comissao'
@ -187,7 +190,7 @@ class ReuniaoCrud(MasterDetailCrud):
public = [RP_LIST, RP_DETAIL, ] public = [RP_LIST, RP_DETAIL, ]
class BaseMixin(MasterDetailCrud.BaseMixin): class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = [ 'nome', 'tema', 'data'] list_field_names = ['data', 'nome', 'tema']
class ListView(MasterDetailCrud.ListView): class ListView(MasterDetailCrud.ListView):
paginate_by = 10 paginate_by = 10
@ -256,6 +259,7 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
form_class = DocumentoAcessorioEditForm form_class = DocumentoAcessorioEditForm
class DeleteView(MasterDetailCrud.DeleteView): class DeleteView(MasterDetailCrud.DeleteView):
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
obj = self.get_object() obj = self.get_object()
obj.delete() obj.delete()

2
sapl/legacy/management/commands/migracao_25_31.py

@ -1,4 +1,3 @@
from django.core import management
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from sapl.legacy.migracao import migrar from sapl.legacy.migracao import migrar
@ -9,5 +8,4 @@ class Command(BaseCommand):
help = 'Migração de dados do SAPL 2.5 para o SAPL 3.1' help = 'Migração de dados do SAPL 2.5 para o SAPL 3.1'
def handle(self, *args, **options): def handle(self, *args, **options):
management.call_command('migrate')
migrar(interativo=False) migrar(interativo=False)

6
sapl/legacy/migracao.py

@ -2,6 +2,7 @@ import subprocess
from getpass import getpass from getpass import getpass
import requests import requests
from django.core import management
from unipath import Path from unipath import Path
from sapl.legacy.migracao_dados import (REPO, TAG_MARCO, gravar_marco, info, from sapl.legacy.migracao_dados import (REPO, TAG_MARCO, gravar_marco, info,
@ -24,11 +25,12 @@ def migrar(interativo=False):
assert TAG_ZOPE in REPO.tags, adornar_msg( assert TAG_ZOPE in REPO.tags, adornar_msg(
'Antes de migrar ' 'Antes de migrar '
'é necessário fazer a exportação de documentos do zope') 'é necessário fazer a exportação de documentos do zope')
migrar_dados(interativo=interativo) management.call_command('migrate')
migrar_dados()
migrar_usuarios(REPO.working_dir) migrar_usuarios(REPO.working_dir)
migrar_documentos(REPO) migrar_documentos(REPO)
gravar_marco() gravar_marco()
compactar_media() # compactar_media()
def compactar_media(): def compactar_media():

70
sapl/legacy/migracao_dados.py

@ -29,7 +29,7 @@ from unipath import Path
from sapl.base.models import AppConfig as AppConf from sapl.base.models import AppConfig as AppConf
from sapl.base.models import Autor, TipoAutor, cria_models_tipo_autor from sapl.base.models import Autor, TipoAutor, cria_models_tipo_autor
from sapl.comissoes.models import Comissao, Composicao, Participacao from sapl.comissoes.models import Comissao, Composicao, Participacao, Reuniao
from sapl.legacy import scripts from sapl.legacy import scripts
from sapl.legacy.models import NormaJuridica as OldNormaJuridica from sapl.legacy.models import NormaJuridica as OldNormaJuridica
from sapl.legacy.models import TipoNumeracaoProtocolo from sapl.legacy.models import TipoNumeracaoProtocolo
@ -58,6 +58,8 @@ from .timezonesbrasil import get_timezone
appconfs = [apps.get_app_config(n) for n in [ appconfs = [apps.get_app_config(n) for n in [
'parlamentares', 'parlamentares',
'comissoes', 'comissoes',
# base precisa vir depois dos apps parlamentares e comissoes
# pois Autor os referencia
'base', 'base',
'materia', 'materia',
'norma', 'norma',
@ -85,38 +87,34 @@ for a1, s1 in name_sets:
# RENAMES ################################################################### # RENAMES ###################################################################
MODEL_RENAME_PATTERN = re.compile('(.+) \((.+)\)') MODEL_RENAME_PATTERN = re.compile('(.+) \((.+)\)')
MODEL_RENAME_INCLUDE_PATTERN = re.compile('<(.+)>')
def get_renames(): def get_renames():
field_renames = {} field_renames = {}
model_renames = {} model_renames = {}
includes = {}
for app in appconfs: for app in appconfs:
app_rename_data = yaml.load( app_rename_data = yaml.load(
pkg_resources.resource_string(app.module.__name__, 'legacy.yaml')) pkg_resources.resource_string(app.module.__name__, 'legacy.yaml'))
for model_name, renames in app_rename_data.items(): for model_name, renames in app_rename_data.items():
# armazena ou substitui includes
if MODEL_RENAME_INCLUDE_PATTERN.match(model_name):
includes[model_name] = renames
continue
elif isinstance(renames, str):
renames = includes[renames]
# detecta mudança de nome
match = MODEL_RENAME_PATTERN.match(model_name) match = MODEL_RENAME_PATTERN.match(model_name)
if match: if match:
model_name, old_name = match.groups() model_name, old_name = match.groups()
else: else:
old_name = None old_name = None
model = getattr(app.models_module, model_name) model = app.get_model(model_name)
if old_name: if old_name:
model_renames[model] = old_name model_renames[model] = old_name
field_renames[model] = renames field_renames[model] = renames
# collect renames from parent classes
for model, renames in field_renames.items():
if any(parent in field_renames for parent in model.__mro__[1:]):
renames = {}
for parent in reversed(model.__mro__):
if parent in field_renames:
renames.update(field_renames[parent])
field_renames[model] = renames
# remove abstract classes
field_renames = {m: r for m, r in field_renames.items()
if not m._meta.abstract}
return field_renames, model_renames return field_renames, model_renames
@ -212,6 +210,10 @@ class ForeignKeyFaltando(ObjectDoesNotExist):
'Uma FK aponta para um registro inexistente' 'Uma FK aponta para um registro inexistente'
def __init__(self, field, valor, old): def __init__(self, field, valor, old):
if (field.related_model.__name__ == 'Comissao'
and old.__class__.__name__ == 'ReuniaoComissao'
and valor == 1):
__import__('pdb').set_trace()
self.field = field self.field = field
self.valor = valor self.valor = valor
self.old = old self.old = old
@ -632,6 +634,7 @@ parlamentar | cod_nivel_instrucao = NULL | cod_nivel_instrucao = 0
parlamentar | tip_situacao_militar = NULL | tip_situacao_militar = 0 parlamentar | tip_situacao_militar = NULL | tip_situacao_militar = 0
mandato | tip_afastamento = NULL | tip_afastamento = 0 mandato | tip_afastamento = NULL | tip_afastamento = 0
relatoria | tip_fim_relatoria = NULL | tip_fim_relatoria = 0 relatoria | tip_fim_relatoria = NULL | tip_fim_relatoria = 0
sessao_plenaria_presenca | dat_sessao = NULL | dat_sessao = 0
'''.strip().splitlines() '''.strip().splitlines()
for spec in update_specs: for spec in update_specs:
@ -794,7 +797,10 @@ def roda_comando_shell(cmd):
assert res == 0, 'O comando falhou: {}'.format(cmd) assert res == 0, 'O comando falhou: {}'.format(cmd)
def migrar_dados(interativo=True): def migrar_dados():
try:
ocorrencias.clear()
ocorrencias.default_factory = list
# restaura dump # restaura dump
arq_dump = Path(DIR_DADOS_MIGRACAO.child( arq_dump = Path(DIR_DADOS_MIGRACAO.child(
@ -813,17 +819,6 @@ def migrar_dados(interativo=True):
uniformiza_banco() uniformiza_banco()
# excluindo database antigo. # excluindo database antigo.
if interativo:
info('Todos os dados do banco serão excluidos. '
'Recomendamos que faça backup do banco sapl '
'antes de continuar.')
info('Deseja continuar? [s/n]')
resposta = input()
if resposta.lower() in ['s', 'sim', 'y', 'yes']:
pass
else:
info('Migração cancelada.')
return 0
info('Excluindo entradas antigas do banco destino.') info('Excluindo entradas antigas do banco destino.')
call([PROJECT_DIR.child('manage.py'), 'flush', call([PROJECT_DIR.child('manage.py'), 'flush',
'--database=default', '--no-input'], stdout=PIPE) '--database=default', '--no-input'], stdout=PIPE)
@ -834,14 +829,13 @@ def migrar_dados(interativo=True):
fill_vinculo_norma_juridica() fill_vinculo_norma_juridica()
fill_dados_basicos() fill_dados_basicos()
info('Começando migração: ...') info('Começando migração: ...')
try:
ocorrencias.clear()
migrar_todos_os_models() migrar_todos_os_models()
except Exception as e: except Exception as e:
ocorrencias['traceback'] = str(traceback.format_exc()) ocorrencias['traceback'] = str(traceback.format_exc())
raise e raise e
finally: finally:
# grava ocorrências # congela e grava ocorrências
ocorrencias.default_factory = None
arq_ocorrencias = Path(REPO.working_dir, 'ocorrencias.yaml') arq_ocorrencias = Path(REPO.working_dir, 'ocorrencias.yaml')
with open(arq_ocorrencias, 'w') as arq: with open(arq_ocorrencias, 'w') as arq:
pyaml.dump(ocorrencias, arq, vspacing=1) pyaml.dump(ocorrencias, arq, vspacing=1)
@ -863,6 +857,10 @@ def move_para_depois_de(lista, movido, referencias):
def get_models_a_migrar(): def get_models_a_migrar():
models = [model for app in appconfs for model in app.models.values() models = [model for app in appconfs for model in app.models.values()
if model in field_renames] if model in field_renames]
# retira reuniões quando não existe na base legada
# (só existe no sapl 3.0)
if 'reuniao_comissao' not in list(exec_legado('show tables')):
models.remove(Reuniao)
# Devido à referência TipoProposicao.tipo_conteudo_related # Devido à referência TipoProposicao.tipo_conteudo_related
# a migração de TipoProposicao precisa ser feita # a migração de TipoProposicao precisa ser feita
# após TipoMateriaLegislativa e TipoDocumento # após TipoMateriaLegislativa e TipoDocumento
@ -1247,6 +1245,17 @@ def adjust_tiporesultadovotacao(new, old):
{'pk': new.pk, 'nome': new.nome}) {'pk': new.pk, 'nome': new.nome})
def str_to_time(fonte):
if not fonte.strip():
return None
tempo = datetime.datetime.strptime(fonte, '%H:%M')
return tempo.time() if tempo else None
def adjust_reuniao_comissao(new, old):
new.hora_inicio = str_to_time(old.hr_inicio_reuniao)
def remove_style(conteudo): def remove_style(conteudo):
if 'style' not in conteudo: if 'style' not in conteudo:
return conteudo # atalho que acelera muito os casos sem style return conteudo # atalho que acelera muito os casos sem style
@ -1284,6 +1293,7 @@ AJUSTE_ANTES_SALVAR = {
Tramitacao: adjust_tramitacao, Tramitacao: adjust_tramitacao,
TipoResultadoVotacao: adjust_tiporesultadovotacao, TipoResultadoVotacao: adjust_tiporesultadovotacao,
ExpedienteSessao: adjust_expediente_sessao, ExpedienteSessao: adjust_expediente_sessao,
Reuniao: adjust_reuniao_comissao,
} }
AJUSTE_DEPOIS_SALVAR = { AJUSTE_DEPOIS_SALVAR = {

36
sapl/legacy/migracao_documentos.py

@ -8,6 +8,7 @@ from django.db import transaction
from image_cropping.fields import ImageCropField from image_cropping.fields import ImageCropField
from sapl.base.models import CasaLegislativa from sapl.base.models import CasaLegislativa
from sapl.comissoes.models import Reuniao
from sapl.legacy.migracao_dados import exec_legado from sapl.legacy.migracao_dados import exec_legado
from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa, from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
Proposicao) Proposicao)
@ -21,7 +22,6 @@ from sapl.sessao.models import SessaoPlenaria
DOCS = { DOCS = {
CasaLegislativa: [('logotipo', 'props_sapl/{}.*')],
Parlamentar: [('fotografia', 'parlamentar/fotos/{}_foto_parlamentar')], Parlamentar: [('fotografia', 'parlamentar/fotos/{}_foto_parlamentar')],
MateriaLegislativa: [('texto_original', 'materia/{}_texto_integral')], MateriaLegislativa: [('texto_original', 'materia/{}_texto_integral')],
DocumentoAcessorio: [('arquivo', 'materia/{}')], DocumentoAcessorio: [('arquivo', 'materia/{}')],
@ -35,24 +35,34 @@ DOCS = {
DocumentoAcessorioAdministrativo: [('arquivo', 'administrativo/{}')], DocumentoAcessorioAdministrativo: [('arquivo', 'administrativo/{}')],
} }
# acrescenta reuniões (que só existem no sapl 3.0)
if 'reuniao_comissao' in set(exec_legado('show tables')):
DOCS[Reuniao] = [('upload_pauta', 'reuniao_comissao/{}_pauta'),
('upload_ata', 'reuniao_comissao/{}_ata')],
DOCS = {model: [(campo, join('sapl_documentos', origem)) DOCS = {model: [(campo, join('sapl_documentos', origem))
for campo, origem, in campos] for campo, origem in campos]
for model, campos in DOCS.items()} for model, campos in DOCS.items()}
def mover_documento(repo, origem, destino): def mover_documento(repo, origem, destino, ignora_origem_ausente=False):
origem, destino = [join(repo.working_dir, c) if not os.path.isabs(c) else c origem, destino = [join(repo.working_dir, c) if not os.path.isabs(c) else c
for c in (origem, destino)] for c in (origem, destino)]
if ignora_origem_ausente and not os.path.exists(origem):
print('Origem ignorada ao mover documento: {}'.format(origem))
return
os.makedirs(os.path.dirname(destino), exist_ok=True) os.makedirs(os.path.dirname(destino), exist_ok=True)
repo.git.mv(origem, destino) repo.git.mv(origem, destino)
def migrar_logotipo(repo, casa, propriedades): def migrar_logotipo(repo, casa, propriedades):
print('.... Migrando logotipo da casa ....') print('.... Migrando logotipo da casa ....')
[(campo, origem)] = DOCS[CasaLegislativa] campo, origem = 'logotipo', 'sapl_documentos/props_sapl/{}.*'
# a extensão do logo pode ter sido ajustada pelo tipo real do arquivo # a extensão do logo pode ter sido ajustada pelo tipo real do arquivo
nome_nas_propriedades = os.path.splitext(propriedades['id_logo'])[0] nome_nas_propriedades = os.path.splitext(propriedades['id_logo'])[0]
arquivos = glob(join(repo.working_dir, origem.format(nome_nas_propriedades))) arquivos = glob(
join(repo.working_dir, origem.format(nome_nas_propriedades)))
if arquivos: if arquivos:
assert len(arquivos) == 1, 'Há mais de um logotipo para a casa' assert len(arquivos) == 1, 'Há mais de um logotipo para a casa'
[logo] = arquivos [logo] = arquivos
@ -95,10 +105,10 @@ def migrar_propriedades_da_casa(repo):
migrar_logotipo(repo, casa, propriedades) migrar_logotipo(repo, casa, propriedades)
casa.save() casa.save()
repo.git.rm(caminho)
def migrar_docs_por_ids(repo, model): def migrar_docs_por_ids(repo, model):
for campo, base_origem in DOCS[model]: for campo, base_origem in DOCS[model]:
print('#### Migrando {} de {} ####'.format(campo, model.__name__)) print('#### Migrando {} de {} ####'.format(campo, model.__name__))
@ -145,7 +155,8 @@ def migrar_documentos(repo):
# Isto significa que para rodar novamente esta função é preciso # Isto significa que para rodar novamente esta função é preciso
# restaurar o repo ao estado anterior # restaurar o repo ao estado anterior
mover_documento(repo, 'XSLT', 'sapl/public/XSLT') mover_documento(repo, 'XSLT', 'sapl/public/XSLT',
ignora_origem_ausente=True)
migrar_propriedades_da_casa(repo) migrar_propriedades_da_casa(repo)
@ -153,16 +164,7 @@ def migrar_documentos(repo):
# (necessário para o cropping de imagem) # (necessário para o cropping de imagem)
repo.git.execute('git annex get sapl_documentos/parlamentar'.split()) repo.git.execute('git annex get sapl_documentos/parlamentar'.split())
for model in [ for model in DOCS:
Parlamentar,
MateriaLegislativa,
DocumentoAcessorio,
NormaJuridica,
SessaoPlenaria,
Proposicao,
DocumentoAdministrativo,
DocumentoAcessorioAdministrativo,
]:
migrar_docs_por_ids(repo, model) migrar_docs_por_ids(repo, model)
sobrando = [join(dir, file) sobrando = [join(dir, file)

11
sapl/legacy/migracao_usuarios.py

@ -95,16 +95,7 @@ def migrar_usuarios(dir_repo):
usuario.groups.add(PERFIL_LEGADO_PARA_NOVO[perfil]) usuario.groups.add(PERFIL_LEGADO_PARA_NOVO[perfil])
usuario.save() usuario.save()
# restringe e configura administradores # configura administradores
if len(admins) > 2:
admins = (
# ususários com admin no nome
[u for u in admins if 'admin' in u.username]
# senão, o usuário saploper, apenas
or [u for u in admins if 'saploper' == u.username]
# senão, simplesmente até os dois primeiros da lista
or admins[:2]
)
for admin in admins: for admin in admins:
admin.is_superuser = True admin.is_superuser = True
admin.save() admin.save()

14
sapl/legacy/models.py

@ -779,6 +779,20 @@ class Relatoria(models.Model):
db_table = 'relatoria' db_table = 'relatoria'
class ReuniaoComissao(models.Model):
cod_reuniao = models.AutoField(primary_key=True)
cod_comissao = models.IntegerField()
num_reuniao = models.IntegerField()
dat_inicio_reuniao = models.DateField()
hr_inicio_reuniao = models.CharField(max_length=5, blank=True, null=True)
txt_observacao = models.TextField(blank=True, null=True)
ind_excluido = models.IntegerField()
class Meta:
managed = False
db_table = 'reuniao_comissao'
class SessaoLegislativa(models.Model): class SessaoLegislativa(models.Model):
cod_sessao_leg = models.AutoField(primary_key=True) cod_sessao_leg = models.AutoField(primary_key=True)
num_legislatura = models.IntegerField() num_legislatura = models.IntegerField()

85
sapl/legacy/scripts/exporta_zope/exporta_zope.py

@ -21,6 +21,7 @@ import ZODB.DB
import ZODB.FileStorage import ZODB.FileStorage
from unipath import Path from unipath import Path
from ZODB.broken import Broken from ZODB.broken import Broken
from ZODB.POSException import POSKeyError
from variaveis_comuns import DIR_DADOS_MIGRACAO, TAG_ZOPE from variaveis_comuns import DIR_DADOS_MIGRACAO, TAG_ZOPE
@ -95,6 +96,9 @@ def guess_extension(fullname, buffer):
return '.DESCONHECIDO.{}'.format(mime.replace('/', '__')) return '.DESCONHECIDO.{}'.format(mime.replace('/', '__'))
CONTEUDO_ARQUIVO_CORROMPIDO = 'ARQUIVO CORROMPIDO'
def get_conteudo_file(doc): def get_conteudo_file(doc):
# A partir daqui usamos dict.pop('...') nos __Broken_state__ # A partir daqui usamos dict.pop('...') nos __Broken_state__
# para contornar um "vazamento" de memória que ocorre # para contornar um "vazamento" de memória que ocorre
@ -105,7 +109,7 @@ def get_conteudo_file(doc):
# #
# Essa medida descarta quase todos os dados retornados # Essa medida descarta quase todos os dados retornados
# e só funciona na primeira passagem # e só funciona na primeira passagem
try:
pdata = br(doc.pop('data')) pdata = br(doc.pop('data'))
if isinstance(pdata, str): if isinstance(pdata, str):
# Retrocedemos se pdata ja eh uma str (necessario em Images) # Retrocedemos se pdata ja eh uma str (necessario em Images)
@ -118,12 +122,17 @@ def get_conteudo_file(doc):
pdata = br(pdata.pop('next', None)) pdata = br(pdata.pop('next', None))
return output.getvalue() return output.getvalue()
except POSKeyError:
return CONTEUDO_ARQUIVO_CORROMPIDO
def dump_file(doc, path, salvar, get_conteudo=get_conteudo_file): def dump_file(doc, path, salvar, get_conteudo=get_conteudo_file):
name = doc['__name__'] name = doc['__name__']
fullname = os.path.join(path, name) fullname = os.path.join(path, name)
conteudo = get_conteudo(doc) conteudo = get_conteudo(doc)
if conteudo == CONTEUDO_ARQUIVO_CORROMPIDO:
fullname = fullname + '_CORROMPIDO'
print('ATENÇÃO: arquivo corrompido: {}'.format(fullname))
if conteudo: if conteudo:
# pula arquivos vazios # pula arquivos vazios
salvar(fullname, conteudo) salvar(fullname, conteudo)
@ -137,7 +146,15 @@ def get_conteudo_dtml_method(doc):
def enumerate_by_key_list(folder, key_list, type_key): def enumerate_by_key_list(folder, key_list, type_key):
for entry in folder.get(key_list, []): for entry in folder.get(key_list, []):
id, meta_type = entry['id'], entry[type_key] id, meta_type = entry['id'], entry[type_key]
try:
obj = br(folder.get(id, None)) obj = br(folder.get(id, None))
except POSKeyError:
print('#' * 80)
print('#' * 80)
print('ATENÇÃO: DIRETÓRIO corrompido: {}'.format(id))
print('#' * 80)
print('#' * 80)
else:
yield id, obj, meta_type yield id, obj, meta_type
@ -186,6 +203,9 @@ def dump_folder(folder, path, salvar, enum=enumerate_folder):
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path) os.makedirs(path)
for id, obj, meta_type in enum(folder): for id, obj, meta_type in enum(folder):
# pula pastas *_old (presentes em várias bases)
if id.endswith('_old') and meta_type in ['Folder', 'BTreeFolder2']:
continue
dump = DUMP_FUNCTIONS.get(meta_type, '?') dump = DUMP_FUNCTIONS.get(meta_type, '?')
if dump == '?': if dump == '?':
nao_identificados[meta_type].append(path + '/' + id) nao_identificados[meta_type].append(path + '/' + id)
@ -271,39 +291,68 @@ def get_app(data_fs_path):
def find_sapl(app): def find_sapl(app):
for obj in app['_objects']: ids_meta_types = [(obj['id'], obj['meta_type']) for obj in app['_objects']]
id, meta_type = obj['id'], obj['meta_type'] # estar ordenado é muito importante para que a busca dê prioridade
# a um id "cm_zzz" antes do id "sapl"
for id, meta_type in sorted(ids_meta_types):
if id.startswith('cm_') and meta_type == 'Folder': if id.startswith('cm_') and meta_type == 'Folder':
cm_zzz = br(app[id]) cm_zzz = br(app[id])
sapl = br(cm_zzz.get('sapl', None)) return find_sapl(cm_zzz)
if sapl and 'sapl_documentos' in sapl and 'acl_users' in sapl: elif id == 'sapl' and meta_type in ['SAPL', 'Folder']:
sapl = br(app['sapl'])
return sapl return sapl
def dump_propriedades(docs, path, salvar, encoding='iso-8859-1'): def detectar_encoding(fonte):
desc = magic.from_buffer(fonte)
for termo, enc in [('ISO-8859', 'latin1'), ('UTF-8', 'utf-8')]:
if termo in desc:
return enc
return None
def autodecode(fonte):
if isinstance(fonte, str):
enc = detectar_encoding(fonte)
return fonte.decode(enc) if enc else fonte
else:
return fonte
def dump_propriedades(docs, path, salvar):
props_sapl = br(docs['props_sapl']) props_sapl = br(docs['props_sapl'])
ids = [p['id'] for p in props_sapl['_properties']] ids = [p['id'] for p in props_sapl['_properties']]
props = {id: props_sapl[id] for id in ids} props = {id: props_sapl[id] for id in ids}
props = {id: p.decode(encoding) if isinstance(p, str) else p props = {id: autodecode(p) for id, p in props.items()}
for id, p in props.items()}
save_as_yaml(path, 'sapl_documentos/propriedades.yaml', props, salvar) save_as_yaml(path, 'sapl_documentos/propriedades.yaml', props, salvar)
def dump_usuarios(sapl, path, salvar): def dump_usuarios(sapl, path, salvar):
users = br(br(sapl['acl_users'])['data']) users = br(br(sapl['acl_users'])['data'])
users = {k: br(v) for k, v in users['data'].items()} users = {autodecode(k): br(v) for k, v in users['data'].items()}
for dados in users.values():
dados['name'] = autodecode(dados['name'])
save_as_yaml(path, 'usuarios.yaml', users, salvar) save_as_yaml(path, 'usuarios.yaml', users, salvar)
def _dump_sapl(data_fs_path, destino, salvar): def _dump_sapl(data_fs_path, documentos_fs_path, destino, salvar):
assert Path(data_fs_path).exists() assert Path(data_fs_path).exists()
assert Path(documentos_fs_path).exists()
app, close_db = get_app(data_fs_path) app, close_db = get_app(data_fs_path)
try: try:
sapl = find_sapl(app) sapl = find_sapl(app)
# extrai folhas XSLT
dump_folder(br(sapl['XSLT']), destino, salvar)
# extrai usuários com suas senhas e perfis # extrai usuários com suas senhas e perfis
dump_usuarios(sapl, destino, salvar) dump_usuarios(sapl, destino, salvar)
finally:
close_db()
app, close_db = get_app(documentos_fs_path)
try:
sapl = find_sapl(app)
# extrai folhas XSLT
if 'XSLT' in sapl:
dump_folder(br(sapl['XSLT']), destino, salvar)
# extrai documentos # extrai documentos
docs = br(sapl['sapl_documentos']) docs = br(sapl['sapl_documentos'])
@ -355,9 +404,15 @@ def build_salvar(repo):
def dump_sapl(sigla): def dump_sapl(sigla):
sigla = sigla[-3:] # ignora prefixo (por ex. 'sapl_cm_') sigla = sigla[-3:] # ignora prefixo (por ex. 'sapl_cm_')
data_fs_path = DIR_DADOS_MIGRACAO.child('datafs', data_fs_path, documentos_fs_path = [
'Data_cm_{}.fs'.format(sigla)) DIR_DADOS_MIGRACAO.child(
'datafs', '{}_cm_{}.fs'.format(prefixo, sigla))
for prefixo in ('Data', 'DocumentosSapl')]
assert data_fs_path.exists(), 'Origem não existe: {}'.format(data_fs_path) assert data_fs_path.exists(), 'Origem não existe: {}'.format(data_fs_path)
if not documentos_fs_path.exists():
documentos_fs_path = data_fs_path
nome_banco_legado = 'sapl_cm_{}'.format(sigla) nome_banco_legado = 'sapl_cm_{}'.format(sigla)
destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado) destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado)
destino.mkdir(parents=True) destino.mkdir(parents=True)
@ -372,7 +427,7 @@ def dump_sapl(sigla):
salvar = build_salvar(repo) salvar = build_salvar(repo)
try: try:
finalizado = False finalizado = False
_dump_sapl(data_fs_path, destino, salvar) _dump_sapl(data_fs_path, documentos_fs_path, destino, salvar)
finalizado = True finalizado = True
finally: finally:
# grava mundaças # grava mundaças

12
sapl/sessao/legacy.yaml

@ -15,7 +15,7 @@ SessaoPlenaria:
url_audio: url_audio url_audio: url_audio
url_video: url_video url_video: url_video
AbstractOrdemDia: <AbstractOrdemDia>:
data_ordem: dat_ordem data_ordem: dat_ordem
materia: cod_materia materia: cod_materia
numero_ordem: num_ordem numero_ordem: num_ordem
@ -24,7 +24,7 @@ AbstractOrdemDia:
sessao_plenaria: cod_sessao_plen sessao_plenaria: cod_sessao_plen
tipo_votacao: tip_votacao tipo_votacao: tip_votacao
ExpedienteMateria: {} ExpedienteMateria: <AbstractOrdemDia>
TipoExpediente: TipoExpediente:
nome: nom_expediente nome: nom_expediente
@ -39,17 +39,17 @@ IntegranteMesa (MesaSessaoPlenaria):
parlamentar: cod_parlamentar parlamentar: cod_parlamentar
sessao_plenaria: cod_sessao_plen sessao_plenaria: cod_sessao_plen
AbstractOrador: <AbstractOrador>:
numero_ordem: num_ordem numero_ordem: num_ordem
parlamentar: cod_parlamentar parlamentar: cod_parlamentar
sessao_plenaria: cod_sessao_plen sessao_plenaria: cod_sessao_plen
url_discurso: url_discurso url_discurso: url_discurso
Orador (Oradores): {} Orador (Oradores): <AbstractOrador>
OradorExpediente (OradoresExpediente): {} OradorExpediente (OradoresExpediente): <AbstractOrador>
OrdemDia: {} OrdemDia: <AbstractOrdemDia>
PresencaOrdemDia (OrdemDiaPresenca): PresencaOrdemDia (OrdemDiaPresenca):
parlamentar: cod_parlamentar parlamentar: cod_parlamentar

Loading…
Cancel
Save