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. 11
      sapl/comissoes/models.py
  5. 34
      sapl/comissoes/views.py
  6. 2
      sapl/legacy/management/commands/migracao_25_31.py
  7. 6
      sapl/legacy/migracao.py
  8. 134
      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. 115
      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
parlamentar: cod_parlamentar
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'),
),
]

11
sapl/comissoes/models.py

@ -184,13 +184,16 @@ class Participacao(models.Model): # ComposicaoComissao
def get_comissao_media_path(instance, subpath, filename):
return './sapl/comissao/%s/%s/%s' % (instance.numero, subpath, filename)
def pauta_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='pauta', pk_first=True)
def ata_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='ata', pk_first=True)
def anexo_upload_path(instance, filename):
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):
periodo = models. ForeignKey(
Periodo,
null=True,
on_delete=models.PROTECT,
verbose_name=_('Periodo da Composicão da Comissão'))
comissao = models.ForeignKey(
@ -211,8 +215,10 @@ class Reuniao(models.Model):
max_length=150, blank=True, verbose_name=_('Tema da Reunião'))
data = models.DateField(verbose_name=_('Data'))
hora_inicio = models.TimeField(
null=True,
verbose_name=_('Horário de Início (hh:mm)'))
hora_fim = models.TimeField(
null=True,
verbose_name=_('Horário de Término (hh:mm)'))
local_reuniao = models.CharField(
max_length=100, blank=True, verbose_name=_('Local da Reunião'))
@ -287,12 +293,13 @@ class Reuniao(models.Model):
@reversion.register()
class DocumentoAcessorio(models.Model):
reuniao = models.ForeignKey(Reuniao,
reuniao = models.ForeignKey(Reuniao,
related_name='documentoacessorio_set',
on_delete=models.PROTECT)
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(
max_length=100, verbose_name=_('Autor'))
ementa = models.TextField(blank=True, verbose_name=_('Ementa'))

34
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.edit import FormMixin
from sapl.base.models import AppConfig as AppsAppConfig
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud,
CrudAux, MasterDetailCrud,
PermissionRequiredForAppCrudMixin)
from sapl.comissoes.forms import (ComissaoForm, ComposicaoForm, DocumentoAcessorioCreateForm,
DocumentoAcessorioEditForm, ParticipacaoCreateForm,
ParticipacaoEditForm, ReuniaoForm, PeriodoForm)
from sapl.comissoes.apps import AppConfig
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)
from sapl.materia.models import MateriaLegislativa, Tramitacao
from .models import (CargoComissao, Comissao, Composicao, DocumentoAcessorio,
Participacao, Periodo, TipoComissao, Reuniao)
from sapl.comissoes.apps import AppConfig
Participacao, Periodo, Reuniao, TipoComissao)
def pegar_url_composicao(pk):
@ -30,6 +30,7 @@ def pegar_url_composicao(pk):
url = reverse('sapl.comissoes:composicao_detail', kwargs={'pk': comp_pk})
return url
def pegar_url_reuniao(pk):
documentoacessorio = DocumentoAcessorio.objects.get(id=pk)
r_pk = documentoacessorio.reuniao.pk
@ -42,6 +43,7 @@ TipoComissaoCrud = CrudAux.build(
TipoComissao, 'tipo_comissao', list_field_names=[
'sigla', 'nome', 'natureza', 'dispositivo_regimental'])
class PeriodoComposicaoCrud(CrudAux):
model = Periodo
@ -77,6 +79,7 @@ class ParticipacaoCrud(MasterDetailCrud):
form_class = ParticipacaoEditForm
class DeleteView(MasterDetailCrud.DeleteView):
def get_success_url(self):
composicao_comissao_pk = self.object.composicao.comissao.pk
composicao_pk = self.object.composicao.pk
@ -93,12 +96,11 @@ class ComposicaoCrud(MasterDetailCrud):
class CreateView(MasterDetailCrud.CreateView):
form_class = ComposicaoForm
def get_initial(self):
comissao = Comissao.objects.get(id=self.kwargs['pk'])
return {'comissao': comissao}
class ListView(MasterDetailCrud.ListView):
template_name = "comissoes/composicao_list.html"
paginate_by = None
@ -180,6 +182,7 @@ class MateriasTramitacaoListView(ListView):
context['object'] = Comissao.objects.get(id=self.kwargs['pk'])
return context
class ReuniaoCrud(MasterDetailCrud):
model = Reuniao
parent_field = 'comissao'
@ -187,7 +190,7 @@ class ReuniaoCrud(MasterDetailCrud):
public = [RP_LIST, RP_DETAIL, ]
class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = [ 'nome', 'tema', 'data']
list_field_names = ['data', 'nome', 'tema']
class ListView(MasterDetailCrud.ListView):
paginate_by = 10
@ -228,9 +231,9 @@ class ReuniaoCrud(MasterDetailCrud):
form_class = ReuniaoForm
def get_initial(self):
comissao = Comissao.objects.get(id=self.kwargs['pk'])
comissao = Comissao.objects.get(id=self.kwargs['pk'])
return {'comissao': comissao}
return {'comissao': comissao}
class DocumentoAcessorioCrud(MasterDetailCrud):
@ -256,6 +259,7 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
form_class = DocumentoAcessorioEditForm
class DeleteView(MasterDetailCrud.DeleteView):
def delete(self, *args, **kwargs):
obj = self.get_object()
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 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'
def handle(self, *args, **options):
management.call_command('migrate')
migrar(interativo=False)

6
sapl/legacy/migracao.py

@ -2,6 +2,7 @@ import subprocess
from getpass import getpass
import requests
from django.core import management
from unipath import Path
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(
'Antes de migrar '
'é 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_documentos(REPO)
gravar_marco()
compactar_media()
# compactar_media()
def compactar_media():

134
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 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.models import NormaJuridica as OldNormaJuridica
from sapl.legacy.models import TipoNumeracaoProtocolo
@ -58,6 +58,8 @@ from .timezonesbrasil import get_timezone
appconfs = [apps.get_app_config(n) for n in [
'parlamentares',
'comissoes',
# base precisa vir depois dos apps parlamentares e comissoes
# pois Autor os referencia
'base',
'materia',
'norma',
@ -85,38 +87,34 @@ for a1, s1 in name_sets:
# RENAMES ###################################################################
MODEL_RENAME_PATTERN = re.compile('(.+) \((.+)\)')
MODEL_RENAME_INCLUDE_PATTERN = re.compile('<(.+)>')
def get_renames():
field_renames = {}
model_renames = {}
includes = {}
for app in appconfs:
app_rename_data = yaml.load(
pkg_resources.resource_string(app.module.__name__, 'legacy.yaml'))
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)
if match:
model_name, old_name = match.groups()
else:
old_name = None
model = getattr(app.models_module, model_name)
model = app.get_model(model_name)
if old_name:
model_renames[model] = old_name
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
@ -212,6 +210,10 @@ class ForeignKeyFaltando(ObjectDoesNotExist):
'Uma FK aponta para um registro inexistente'
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.valor = valor
self.old = old
@ -626,12 +628,13 @@ def uniformiza_banco():
''')
update_specs = '''
vinculo_norma_juridica| ind_excluido = '' | trim(ind_excluido) = '0'
unidade_tramitacao | cod_parlamentar = NULL | cod_parlamentar = 0
parlamentar | cod_nivel_instrucao = NULL | cod_nivel_instrucao = 0
parlamentar | tip_situacao_militar = NULL | tip_situacao_militar = 0
mandato | tip_afastamento = NULL | tip_afastamento = 0
relatoria | tip_fim_relatoria = NULL | tip_fim_relatoria = 0
vinculo_norma_juridica | ind_excluido = '' | trim(ind_excluido) = '0'
unidade_tramitacao | cod_parlamentar = NULL | cod_parlamentar = 0
parlamentar | cod_nivel_instrucao = NULL | cod_nivel_instrucao = 0
parlamentar | tip_situacao_militar = NULL | tip_situacao_militar = 0
mandato | tip_afastamento = NULL | tip_afastamento = 0
relatoria | tip_fim_relatoria = NULL | tip_fim_relatoria = 0
sessao_plenaria_presenca | dat_sessao = NULL | dat_sessao = 0
'''.strip().splitlines()
for spec in update_specs:
@ -794,54 +797,45 @@ def roda_comando_shell(cmd):
assert res == 0, 'O comando falhou: {}'.format(cmd)
def migrar_dados(interativo=True):
# restaura dump
arq_dump = Path(DIR_DADOS_MIGRACAO.child(
'dumps_mysql', '{}.sql'.format(NOME_BANCO_LEGADO)))
assert arq_dump.exists(), 'Dump do mysql faltando: {}'.format(arq_dump)
info('Restaurando dump mysql de [{}]'.format(arq_dump))
normaliza_dump_mysql(arq_dump)
roda_comando_shell('mysql -uroot < {}'.format(arq_dump))
# executa ajustes pré-migração, se existirem
arq_ajustes_pre_migracao = DIR_DADOS_MIGRACAO.child(
'ajustes_pre_migracao', '{}.sql'.format(sigla_casa))
if arq_ajustes_pre_migracao.exists():
exec_legado(arq_ajustes_pre_migracao.read_file())
uniformiza_banco()
# 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.')
call([PROJECT_DIR.child('manage.py'), 'flush',
'--database=default', '--no-input'], stdout=PIPE)
# apaga tipos de autor padrão (criados no flush acima)
TipoAutor.objects.all().delete()
fill_vinculo_norma_juridica()
fill_dados_basicos()
info('Começando migração: ...')
def migrar_dados():
try:
ocorrencias.clear()
ocorrencias.default_factory = list
# restaura dump
arq_dump = Path(DIR_DADOS_MIGRACAO.child(
'dumps_mysql', '{}.sql'.format(NOME_BANCO_LEGADO)))
assert arq_dump.exists(), 'Dump do mysql faltando: {}'.format(arq_dump)
info('Restaurando dump mysql de [{}]'.format(arq_dump))
normaliza_dump_mysql(arq_dump)
roda_comando_shell('mysql -uroot < {}'.format(arq_dump))
# executa ajustes pré-migração, se existirem
arq_ajustes_pre_migracao = DIR_DADOS_MIGRACAO.child(
'ajustes_pre_migracao', '{}.sql'.format(sigla_casa))
if arq_ajustes_pre_migracao.exists():
exec_legado(arq_ajustes_pre_migracao.read_file())
uniformiza_banco()
# excluindo database antigo.
info('Excluindo entradas antigas do banco destino.')
call([PROJECT_DIR.child('manage.py'), 'flush',
'--database=default', '--no-input'], stdout=PIPE)
# apaga tipos de autor padrão (criados no flush acima)
TipoAutor.objects.all().delete()
fill_vinculo_norma_juridica()
fill_dados_basicos()
info('Começando migração: ...')
migrar_todos_os_models()
except Exception as e:
ocorrencias['traceback'] = str(traceback.format_exc())
raise e
finally:
# grava ocorrências
# congela e grava ocorrências
ocorrencias.default_factory = None
arq_ocorrencias = Path(REPO.working_dir, 'ocorrencias.yaml')
with open(arq_ocorrencias, 'w') as arq:
pyaml.dump(ocorrencias, arq, vspacing=1)
@ -863,6 +857,10 @@ def move_para_depois_de(lista, movido, referencias):
def get_models_a_migrar():
models = [model for app in appconfs for model in app.models.values()
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
# a migração de TipoProposicao precisa ser feita
# após TipoMateriaLegislativa e TipoDocumento
@ -1247,6 +1245,17 @@ def adjust_tiporesultadovotacao(new, old):
{'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):
if 'style' not in conteudo:
return conteudo # atalho que acelera muito os casos sem style
@ -1284,6 +1293,7 @@ AJUSTE_ANTES_SALVAR = {
Tramitacao: adjust_tramitacao,
TipoResultadoVotacao: adjust_tiporesultadovotacao,
ExpedienteSessao: adjust_expediente_sessao,
Reuniao: adjust_reuniao_comissao,
}
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 sapl.base.models import CasaLegislativa
from sapl.comissoes.models import Reuniao
from sapl.legacy.migracao_dados import exec_legado
from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
Proposicao)
@ -21,7 +22,6 @@ from sapl.sessao.models import SessaoPlenaria
DOCS = {
CasaLegislativa: [('logotipo', 'props_sapl/{}.*')],
Parlamentar: [('fotografia', 'parlamentar/fotos/{}_foto_parlamentar')],
MateriaLegislativa: [('texto_original', 'materia/{}_texto_integral')],
DocumentoAcessorio: [('arquivo', 'materia/{}')],
@ -35,24 +35,34 @@ DOCS = {
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))
for campo, origem, in campos]
for campo, origem in campos]
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
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)
repo.git.mv(origem, destino)
def migrar_logotipo(repo, casa, propriedades):
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
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:
assert len(arquivos) == 1, 'Há mais de um logotipo para a casa'
[logo] = arquivos
@ -95,10 +105,10 @@ def migrar_propriedades_da_casa(repo):
migrar_logotipo(repo, casa, propriedades)
casa.save()
repo.git.rm(caminho)
def migrar_docs_por_ids(repo, model):
for campo, base_origem in DOCS[model]:
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
# 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)
@ -153,16 +164,7 @@ def migrar_documentos(repo):
# (necessário para o cropping de imagem)
repo.git.execute('git annex get sapl_documentos/parlamentar'.split())
for model in [
Parlamentar,
MateriaLegislativa,
DocumentoAcessorio,
NormaJuridica,
SessaoPlenaria,
Proposicao,
DocumentoAdministrativo,
DocumentoAcessorioAdministrativo,
]:
for model in DOCS:
migrar_docs_por_ids(repo, model)
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.save()
# restringe e 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]
)
# configura administradores
for admin in admins:
admin.is_superuser = True
admin.save()

14
sapl/legacy/models.py

@ -779,6 +779,20 @@ class Relatoria(models.Model):
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):
cod_sessao_leg = models.AutoField(primary_key=True)
num_legislatura = models.IntegerField()

115
sapl/legacy/scripts/exporta_zope/exporta_zope.py

@ -21,6 +21,7 @@ import ZODB.DB
import ZODB.FileStorage
from unipath import Path
from ZODB.broken import Broken
from ZODB.POSException import POSKeyError
from variaveis_comuns import DIR_DADOS_MIGRACAO, TAG_ZOPE
@ -95,6 +96,9 @@ def guess_extension(fullname, buffer):
return '.DESCONHECIDO.{}'.format(mime.replace('/', '__'))
CONTEUDO_ARQUIVO_CORROMPIDO = 'ARQUIVO CORROMPIDO'
def get_conteudo_file(doc):
# A partir daqui usamos dict.pop('...') nos __Broken_state__
# para contornar um "vazamento" de memória que ocorre
@ -105,25 +109,30 @@ def get_conteudo_file(doc):
#
# Essa medida descarta quase todos os dados retornados
# e só funciona na primeira passagem
try:
pdata = br(doc.pop('data'))
if isinstance(pdata, str):
# Retrocedemos se pdata ja eh uma str (necessario em Images)
doc['data'] = pdata
pdata = doc
pdata = br(doc.pop('data'))
if isinstance(pdata, str):
# Retrocedemos se pdata ja eh uma str (necessario em Images)
doc['data'] = pdata
pdata = doc
output = cStringIO.StringIO()
while pdata:
output.write(pdata.pop('data'))
pdata = br(pdata.pop('next', None))
output = cStringIO.StringIO()
while pdata:
output.write(pdata.pop('data'))
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):
name = doc['__name__']
fullname = os.path.join(path, name)
conteudo = get_conteudo(doc)
if conteudo == CONTEUDO_ARQUIVO_CORROMPIDO:
fullname = fullname + '_CORROMPIDO'
print('ATENÇÃO: arquivo corrompido: {}'.format(fullname))
if conteudo:
# pula arquivos vazios
salvar(fullname, conteudo)
@ -137,8 +146,16 @@ def get_conteudo_dtml_method(doc):
def enumerate_by_key_list(folder, key_list, type_key):
for entry in folder.get(key_list, []):
id, meta_type = entry['id'], entry[type_key]
obj = br(folder.get(id, None))
yield id, obj, meta_type
try:
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
enumerate_folder = partial(enumerate_by_key_list,
@ -186,6 +203,9 @@ def dump_folder(folder, path, salvar, enum=enumerate_folder):
if not os.path.exists(path):
os.makedirs(path)
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, '?')
if dump == '?':
nao_identificados[meta_type].append(path + '/' + id)
@ -271,39 +291,68 @@ def get_app(data_fs_path):
def find_sapl(app):
for obj in app['_objects']:
id, meta_type = obj['id'], obj['meta_type']
ids_meta_types = [(obj['id'], obj['meta_type']) for obj in app['_objects']]
# 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':
cm_zzz = br(app[id])
sapl = br(cm_zzz.get('sapl', None))
if sapl and 'sapl_documentos' in sapl and 'acl_users' in sapl:
return sapl
return find_sapl(cm_zzz)
elif id == 'sapl' and meta_type in ['SAPL', 'Folder']:
sapl = br(app['sapl'])
return sapl
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, encoding='iso-8859-1'):
def dump_propriedades(docs, path, salvar):
props_sapl = br(docs['props_sapl'])
ids = [p['id'] for p in props_sapl['_properties']]
props = {id: props_sapl[id] for id in ids}
props = {id: p.decode(encoding) if isinstance(p, str) else p
for id, p in props.items()}
props = {id: autodecode(p) for id, p in props.items()}
save_as_yaml(path, 'sapl_documentos/propriedades.yaml', props, salvar)
def dump_usuarios(sapl, path, salvar):
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)
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(documentos_fs_path).exists()
app, close_db = get_app(data_fs_path)
try:
sapl = find_sapl(app)
# extrai folhas XSLT
dump_folder(br(sapl['XSLT']), destino, salvar)
# extrai usuários com suas senhas e perfis
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
docs = br(sapl['sapl_documentos'])
@ -355,9 +404,15 @@ def build_salvar(repo):
def dump_sapl(sigla):
sigla = sigla[-3:] # ignora prefixo (por ex. 'sapl_cm_')
data_fs_path = DIR_DADOS_MIGRACAO.child('datafs',
'Data_cm_{}.fs'.format(sigla))
data_fs_path, documentos_fs_path = [
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)
if not documentos_fs_path.exists():
documentos_fs_path = data_fs_path
nome_banco_legado = 'sapl_cm_{}'.format(sigla)
destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado)
destino.mkdir(parents=True)
@ -372,7 +427,7 @@ def dump_sapl(sigla):
salvar = build_salvar(repo)
try:
finalizado = False
_dump_sapl(data_fs_path, destino, salvar)
_dump_sapl(data_fs_path, documentos_fs_path, destino, salvar)
finalizado = True
finally:
# grava mundaças
@ -382,8 +437,8 @@ def dump_sapl(sigla):
# se de fato existe mudança
status = 'completa' if finalizado else 'parcial'
repo.index.commit(u'Exportação do zope {}'.format(status))
if finalizado:
repo.git.execute('git tag -f'.split() + [TAG_ZOPE])
if finalizado:
repo.git.execute('git tag -f'.split() + [TAG_ZOPE])
if __name__ == "__main__":

12
sapl/sessao/legacy.yaml

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

Loading…
Cancel
Save