From 825e27c1dd44a041da729920a65a8ffde839d20e Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Thu, 5 Apr 2018 17:42:59 -0300 Subject: [PATCH 01/19] =?UTF-8?q?Adiciona=20mais=20propaga=C3=A7=C3=B5es?= =?UTF-8?q?=20de=20dele=C3=A7=C3=B5es=20pr=C3=A9-migra=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/legacy/migracao_dados.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py index 9b2846fa7..6b10e51ee 100644 --- a/sapl/legacy/migracao_dados.py +++ b/sapl/legacy/migracao_dados.py @@ -201,7 +201,7 @@ class ForeignKeyFaltando(ObjectDoesNotExist): campo = campos_novos_para_antigos[self.field] _, tabela, campos_pk = get_estrutura_legado(self.field.model) pk = {c: getattr(self.old, c) for c in campos_pk} - sql = 'select * from {} where {}'.format( + sql = 'select * from {} where {};'.format( tabela, ' and '.join(['{} = {}'.format(k, v) for k, v in pk.items()])) return OrderedDict((('campo', campo), @@ -494,6 +494,8 @@ PROPAGACOES_DE_EXCLUSAO = [ ('parlamentar', 'dependente', 'cod_parlamentar'), ('parlamentar', 'filiacao', 'cod_parlamentar'), ('parlamentar', 'mandato', 'cod_parlamentar'), + ('parlamentar', 'composicao_mesa', 'cod_parlamentar'), + ('parlamentar', 'composicao_comissao', 'cod_parlamentar'), # comissao ('comissao', 'composicao_comissao', 'cod_comissao'), @@ -518,6 +520,11 @@ PROPAGACOES_DE_EXCLUSAO = [ ('materia_legislativa', 'anexada', 'cod_materia_principal'), ('materia_legislativa', 'anexada', 'cod_materia_anexada'), ('materia_legislativa', 'documento_acessorio', 'cod_materia'), + ('materia_legislativa', 'numeracao', 'cod_materia'), + + # norma + ('norma_juridica', 'vinculo_norma_juridica', 'cod_norma_referente'), + ('norma_juridica', 'vinculo_norma_juridica', 'cod_norma_referida'), # documento administrativo ('documento_administrativo', 'tramitacao_administrativo', 'cod_documento'), @@ -800,7 +807,7 @@ def migrar_dados(interativo=True): arq_ocorrencias = dir_ocorrencias.child( nome_banco_legado + '.yaml') with open(arq_ocorrencias, 'w') as arq: - dump = yaml.dump(dict(ocorrencias), allow_unicode=True) + dump = yaml.dump(dict(ocorrencias), allow_unicode=True, width=1000) arq.write(dump.replace('\n- ', '\n\n- ')) info('Ocorrências salvas em\n {}'.format(arq_ocorrencias)) From 09085b88a2780fc76ff9194d1298792812a65740 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Fri, 6 Apr 2018 12:55:08 -0300 Subject: [PATCH 02/19] Corrige rehash de senhas importadas do zope --- sapl/hashers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sapl/hashers.py b/sapl/hashers.py index e80642def..3e9cf6c5f 100644 --- a/sapl/hashers.py +++ b/sapl/hashers.py @@ -46,11 +46,12 @@ ZOPE_SHA1_PREFIX = '{SSHA}' def zope_encoded_password_to_django(encoded): "Migra um hash de senha do zope para uso com o ZopeSHA1PasswordHasher" - if encoded.startswith(ZOPE_SHA1_PREFIX): + if encoded and encoded.startswith(ZOPE_SHA1_PREFIX): data = encoded[len(ZOPE_SHA1_PREFIX):] salt = get_salt_from_zope_sha1(data) hasher = ZopeSHA1PasswordHasher() return super(ZopeSHA1PasswordHasher, hasher).encode(data, salt) else: # assume it's a plain password and use the default hashing + # a None password blocks login, forcing a password reset return make_password(encoded) From 1cf2f35def5025bcf9b3bd3ee9e1c184a5e317c5 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Fri, 6 Apr 2018 19:08:54 -0300 Subject: [PATCH 03/19] =?UTF-8?q?Corrige=20caminhos=20de=20arquivos=20na?= =?UTF-8?q?=20migra=C3=A7=C3=A3o=20de=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/legacy/migracao.py | 6 -- sapl/legacy/migracao_documentos.py | 134 ++++++++--------------------- 2 files changed, 36 insertions(+), 104 deletions(-) diff --git a/sapl/legacy/migracao.py b/sapl/legacy/migracao.py index 2690e9a53..be8830aa8 100644 --- a/sapl/legacy/migracao.py +++ b/sapl/legacy/migracao.py @@ -14,12 +14,6 @@ def migrar(interativo=False): migrar_documentos() -# fonte: https://stackoverflow.com/a/17081026/1877490 -def make_tarfile(output_filename, source_dir): - with tarfile.open(output_filename, "w:gz") as tar: - tar.add(source_dir, arcname=os.path.basename(source_dir)) - - def gerar_pacote(): banco = settings.DATABASES['legacy']['NAME'] diff --git a/sapl/legacy/migracao_documentos.py b/sapl/legacy/migracao_documentos.py index 9ae46ef5d..a94b9160a 100644 --- a/sapl/legacy/migracao_documentos.py +++ b/sapl/legacy/migracao_documentos.py @@ -1,12 +1,12 @@ -import mimetypes import os import re from glob import glob import yaml +from django.db import transaction from sapl.base.models import CasaLegislativa -from sapl.legacy.migracao_dados import exec_legado, warn +from sapl.legacy.migracao_dados import exec_legado from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa, Proposicao) from sapl.norma.models import NormaJuridica @@ -16,88 +16,26 @@ from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo, from sapl.sessao.models import SessaoPlenaria from sapl.settings import MEDIA_ROOT - # MIGRAÇÃO DE DOCUMENTOS ################################################### -def get_ano(obj): - return [obj.ano] - - -def ___(obj): - return [] - - DOCS = { - CasaLegislativa: [ - ('logotipo', - 'props_sapl/{}.*', - 'public/casa/logotipo/', - ___) - ], - Parlamentar: [ - ('fotografia', - 'parlamentar/fotos/{}_foto_parlamentar', - 'public/parlamentar/{0}/', - ___) - ], - MateriaLegislativa: [ - ('texto_original', - 'materia/{}_texto_integral', - 'public/materialegislativa/{1}/{0}/', - get_ano) - ], - DocumentoAcessorio: [ - ('arquivo', - 'materia/{}', - 'public/documentoacessorio/{1}/{0}/', - lambda obj: [obj.materia.ano]) - ], - NormaJuridica: [ - ('texto_integral', - 'norma_juridica/{}_texto_integral', - 'public/normajuridica/{1}/{0}/', - get_ano) - ], - SessaoPlenaria: [ - ('upload_pauta', - 'pauta_sessao/{}_pauta_sessao', - 'public/sessaoplenaria/{0}/pauta/', - ___), - ('upload_ata', - 'ata_sessao/{}_ata_sessao', - 'public/sessaoplenaria/{0}/ata/', - ___), - ('upload_anexo', - 'anexo_sessao/{}_texto_anexado', - 'public/sessaoplenaria/{0}/anexo/', - ___) - ], - Proposicao: [ - ('texto_original', - 'proposicao/{}', - 'private/proposicao/{0}/', - get_ano) - ], - DocumentoAdministrativo: [ - ('texto_integral', - 'administrativo/{}_texto_integral', - 'private/documentoadministrativo/{0}/', - get_ano) - ], - DocumentoAcessorioAdministrativo: [ - ('arquivo', - 'administrativo/{}', - 'private/documentoacessorioadministrativo/{0}/', - ___) - ], + CasaLegislativa: [('logotipo', 'props_sapl/{}.*')], + Parlamentar: [('fotografia', 'parlamentar/fotos/{}_foto_parlamentar')], + MateriaLegislativa: [('texto_original', 'materia/{}_texto_integral')], + DocumentoAcessorio: [('arquivo', 'materia/{}')], + NormaJuridica: [('texto_integral', 'norma_juridica/{}_texto_integral')], + SessaoPlenaria: [('upload_pauta', 'pauta_sessao/{}_pauta_sessao'), + ('upload_ata', 'ata_sessao/{}_ata_sessao'), + ('upload_anexo', 'anexo_sessao/{}_texto_anexado')], + Proposicao: [('texto_original', 'proposicao/{}')], + DocumentoAdministrativo: [('texto_integral', + 'administrativo/{}_texto_integral')], + DocumentoAcessorioAdministrativo: [('arquivo', 'administrativo/{}')], } -DOCS = {model: [(campo, - os.path.join('sapl_documentos', origem), - os.path.join('sapl', destino), - get_extra_args) - for campo, origem, destino, get_extra_args in campos] +DOCS = {model: [(campo, os.path.join('sapl_documentos', origem)) + for campo, origem, in campos] for model, campos in DOCS.items()} @@ -141,11 +79,13 @@ def migrar_propriedades_da_casa(): [(casa.municipio, casa.uf)] = exec_legado(sql_localidade) print('.... Migrando logotipo da casa ....') - [(_, origem, destino, __)] = DOCS[CasaLegislativa] + [(campo, origem)] = DOCS[CasaLegislativa] # a extensão do logo pode ter sido ajustada pelo tipo real do arquivo id_logo = os.path.splitext(propriedades['id_logo'])[0] [origem] = glob(em_media(origem.format(id_logo))) - destino = os.path.join(destino, os.path.basename(origem)) + destino = os.path.join( + CasaLegislativa._meta.get_field(campo).upload_to, + os.path.basename(origem)) mover_documento(origem, destino) casa.logotipo = destino casa.save() @@ -153,36 +93,36 @@ def migrar_propriedades_da_casa(): def migrar_docs_por_ids(model): - for campo, base_origem, base_destino, get_extra_args in DOCS[model]: + for campo, base_origem in DOCS[model]: print('#### Migrando {} de {} ####'.format(campo, model.__name__)) dir_origem, nome_origem = os.path.split(em_media(base_origem)) nome_origem = nome_origem.format('(\d+)') pat = re.compile('^{}\.\w+$'.format(nome_origem)) - if not os.path.isdir(dir_origem): print(' >>> O diretório {} não existe! Abortado.'.format( dir_origem)) continue - for arq in os.listdir(dir_origem): - match = pat.match(arq) - if match: + matches = [pat.match(arq) for arq in os.listdir(dir_origem)] + ids_origens = [(int(m.group(1)), + os.path.join(dir_origem, m.group(0))) + for m in matches if m] + objetos = {obj.id: obj for obj in model.objects.all()} + upload_to = model._meta.get_field(campo).upload_to + + with transaction.atomic(): + for id, origem in ids_origens: # associa documento ao objeto - origem = os.path.join(dir_origem, match.group(0)) - id = match.group(1) - try: - obj = model.objects.get(pk=id) - except model.DoesNotExist: - msg = ' {} (pk={}) não encontrado para documento em [{}]' - print(msg.format(model.__name__, id, origem)) - else: - destino = os.path.join( - base_destino.format(id, *get_extra_args(obj)), - os.path.basename(origem)) + obj = objetos.get(id) + if obj: + destino = upload_to(obj, os.path.basename(origem)) mover_documento(origem, destino) setattr(obj, campo, destino) obj.save() + else: + msg = ' {} (pk={}) não encontrado para documento em [{}]' + print(msg.format(model.__name__, id, origem)) def migrar_documentos(): @@ -215,5 +155,3 @@ def migrar_documentos(): print('\n#### Encerrado ####\n\n' '{} documentos sobraram sem ser migrados!!!'.format( len(sobrando))) - for doc in sobrando: - print(' {}'. format(doc)) From 500574a82044fcf410178d874c6206837e50e02c Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Mon, 9 Apr 2018 14:42:35 -0300 Subject: [PATCH 04/19] =?UTF-8?q?Usa=20pretty-yaml=20p=20registro=20de=20o?= =?UTF-8?q?corr=C3=AAncias=20de=20migra=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements/migration-requirements.txt | 1 + sapl/legacy/migracao_dados.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/requirements/migration-requirements.txt b/requirements/migration-requirements.txt index d5793dad8..fbe9543e2 100644 --- a/requirements/migration-requirements.txt +++ b/requirements/migration-requirements.txt @@ -1,2 +1,3 @@ -r dev-requirements.txt mysqlclient==1.3.12 +pyaml diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py index 6b10e51ee..dd6e4f8d1 100644 --- a/sapl/legacy/migracao_dados.py +++ b/sapl/legacy/migracao_dados.py @@ -8,6 +8,7 @@ from operator import xor from subprocess import PIPE, call import pkg_resources +import pyaml import pytz import reversion import yaml @@ -807,8 +808,7 @@ def migrar_dados(interativo=True): arq_ocorrencias = dir_ocorrencias.child( nome_banco_legado + '.yaml') with open(arq_ocorrencias, 'w') as arq: - dump = yaml.dump(dict(ocorrencias), allow_unicode=True, width=1000) - arq.write(dump.replace('\n- ', '\n\n- ')) + pyaml.dump(ocorrencias, arq, vspacing=1) info('Ocorrências salvas em\n {}'.format(arq_ocorrencias)) # recria tipos de autor padrão que não foram criados pela migração From 70bc3148885f1c98fc8ec4beda7505b312733625 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Mon, 9 Apr 2018 14:57:02 -0300 Subject: [PATCH 05/19] Ignora app legacy no shell_plus --- sapl/legacy_migration_settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sapl/legacy_migration_settings.py b/sapl/legacy_migration_settings.py index 7d911bf7e..0d8844147 100644 --- a/sapl/legacy_migration_settings.py +++ b/sapl/legacy_migration_settings.py @@ -33,3 +33,5 @@ DEBUG = True # delisga indexação fulltext em tempo real HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.BaseSignalProcessor' + +SHELL_PLUS_DONT_LOAD = ['legacy'] From 5ef440685eda05e1052acd9d585b2b337fc76977 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Mon, 9 Apr 2018 18:05:11 -0300 Subject: [PATCH 06/19] Adiciona gravacao de marco da migracao --- sapl/legacy/migracao_dados.py | 44 ++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py index dd6e4f8d1..ceaea8633 100644 --- a/sapl/legacy/migracao_dados.py +++ b/sapl/legacy/migracao_dados.py @@ -1,3 +1,4 @@ +import datetime import re import traceback from collections import OrderedDict, defaultdict, namedtuple @@ -19,6 +20,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist from django.db import connections, transaction from django.db.models import Max, Q +from pyaml import UnsafePrettyYAMLDumper from unipath import Path from sapl.base.models import AppConfig as AppConf @@ -823,7 +825,7 @@ def move_para_depois_de(lista, movido, referencias): return lista -def migrar_todos_os_models(): +def get_models_a_migrar(): models = [model for app in appconfs for model in app.models.values() if model in field_renames] # Devido à referência TipoProposicao.tipo_conteudo_related @@ -836,7 +838,11 @@ def migrar_todos_os_models(): move_para_depois_de(models, Proposicao, [MateriaLegislativa, DocumentoAdministrativo]) - for model in models: + return models + + +def migrar_todos_os_models(): + for model in get_models_a_migrar(): migrar_model(model) @@ -1233,4 +1239,36 @@ AJUSTE_DEPOIS_SALVAR = { NormaJuridica: adjust_normajuridica_depois_salvar, } -# CHECKS #################################################################### + +# MARCO ###################################################################### + +TIME_FORMAT = '%H:%M:%S' + + +def time_representer(dumper, data): + return dumper.represent_scalar('!time', data.strftime(TIME_FORMAT)) +UnsafePrettyYAMLDumper.add_representer(datetime.time, time_representer) + + +def time_constructor(loader, node): + value = loader.construct_scalar(node) + return datetime.datetime.strptime(value, TIME_FORMAT).time() +yaml.add_constructor(u'!time', time_constructor) + + +DIR_MARCO = Path(DIR_DADOS_MIGRACAO, 'marcos', nome_banco_legado) + + +def grava_marco_base(): + user_model = get_user_model() + models = get_models_a_migrar() + [ + Composicao, user_model, Group, ContentType] + for model in models: + info('Gravando marco de [{}]'.format(model.__name__)) + dir_model = Path( + DIR_MARCO, 'dados', model._meta.app_label, model.__name__) + dir_model.mkdir(parents=True) + for data in model.objects.all().values(): + nome_arq = Path(dir_model, data['id']) + with open(nome_arq, 'w') as arq: + pyaml.dump(data, arq) From acc9cbe2626cd2469d94fd80531e2af310e369ac Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Mon, 9 Apr 2018 18:12:24 -0300 Subject: [PATCH 07/19] =?UTF-8?q?Move=20migrate=20p=20script=20de=20recria?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20bancos=20postgres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/legacy/scripts/migra_um_db.sh | 5 ----- sapl/legacy/scripts/recria_um_db_postgres.sh | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sapl/legacy/scripts/migra_um_db.sh b/sapl/legacy/scripts/migra_um_db.sh index ed4db9662..b5b336cd2 100755 --- a/sapl/legacy/scripts/migra_um_db.sh +++ b/sapl/legacy/scripts/migra_um_db.sh @@ -32,11 +32,6 @@ if [ $# -ge 2 ]; then echo "O banco legado foi restaurado" |& tee -a $LOG echo >> $LOG - echo "--- DJANGO MIGRATE ---" | tee -a $LOG - echo >> $LOG - DATABASE_NAME=$1 ./manage.py migrate --settings sapl.legacy_migration_settings - echo >> $LOG - echo "--- MIGRACAO ---" | tee -a $LOG echo >> $LOG DATABASE_NAME=$1 ./manage.py migracao_25_31 --force --dados --settings sapl.legacy_migration_settings 2>&1 | tee -a $LOG diff --git a/sapl/legacy/scripts/recria_um_db_postgres.sh b/sapl/legacy/scripts/recria_um_db_postgres.sh index 3ff66e8f3..98defaaa6 100755 --- a/sapl/legacy/scripts/recria_um_db_postgres.sh +++ b/sapl/legacy/scripts/recria_um_db_postgres.sh @@ -4,3 +4,8 @@ echo "Database $1" sudo -u postgres psql -c "drop DATABASE if exists $1" sudo -u postgres psql -c "CREATE DATABASE $1 WITH OWNER = sapl ENCODING = 'UTF8' TABLESPACE = pg_default LC_COLLATE = 'pt_BR.UTF-8' LC_CTYPE = 'pt_BR.UTF-8' CONNECTION LIMIT = -1 TEMPLATE template0;" + + +echo "--- DJANGO MIGRATE ---" | tee -a $LOG +DATABASE_NAME=$1 ./manage.py migrate --settings sapl.legacy_migration_settings + From 444e9dacad43b672975bc75e392dc6f0366b451c Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Mon, 9 Apr 2018 18:27:31 -0300 Subject: [PATCH 08/19] Atualiza assinatura de LegacyRouter.allow_migrate --- sapl/legacy/router.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapl/legacy/router.py b/sapl/legacy/router.py index 68b235059..1aa51a46b 100644 --- a/sapl/legacy/router.py +++ b/sapl/legacy/router.py @@ -16,7 +16,7 @@ class LegacyRouter: return True return None - def allow_migrate(self, db, model): - if model._meta.app_label == 'legacy': + def allow_migrate(self, db, app_label, model_name=None, **hints): + if app_label == 'legacy': return False return None From 27b4096f46c6b5df9be66d0e39eabbdbfef38777 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Tue, 10 Apr 2018 13:15:02 -0300 Subject: [PATCH 09/19] Corrige ajuste de tipo de proposicao antes de salvar --- sapl/legacy/migracao_dados.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py index ceaea8633..f0bf710f0 100644 --- a/sapl/legacy/migracao_dados.py +++ b/sapl/legacy/migracao_dados.py @@ -146,6 +146,15 @@ CAMPOS_VIRTUAIS_PROPOSICAO = { for campo_virtual in CAMPOS_VIRTUAIS_PROPOSICAO.values(): campos_novos_para_antigos[campo_virtual] = 'cod_mat_ou_doc' + +CAMPOS_VIRTUAIS_TIPO_PROPOSICAO = { + 'M': CampoVirtual(TipoProposicao, TipoMateriaLegislativa), + 'D': CampoVirtual(TipoProposicao, TipoDocumento) +} +for campo_virtual in CAMPOS_VIRTUAIS_TIPO_PROPOSICAO.values(): + campos_novos_para_antigos[campo_virtual] = 'tip_mat_ou_doc' + + # campos virtuais de Autor para funcionar com get_fk_related CAMPOS_VIRTUAIS_AUTOR = {related: CampoVirtual(Autor, related) for related in (Parlamentar, Comissao, Partido)} @@ -1074,22 +1083,16 @@ def adjust_tipoafastamento(new, old): new.indicador = 'F' -TIPO_MATERIA_OU_TIPO_DOCUMENTO = {'M': TipoMateriaLegislativa, - 'D': TipoDocumento} +def set_generic_fk(new, campo_virtual, old): + new.content_type = content_types[campo_virtual.related_model] + new.object_id = get_fk_related(campo_virtual, old) def adjust_tipoproposicao(new, old): "Aponta para o tipo relacionado de matéria ou documento" - value = old.tip_mat_ou_doc - model_tipo = TIPO_MATERIA_OU_TIPO_DOCUMENTO[old.ind_mat_ou_doc] - tipo = model_tipo.objects.filter(pk=value) - if tipo: - new.tipo_conteudo_related = tipo[0] - else: - raise ForeignKeyFaltando( - field=TipoProposicao.tipo_conteudo_related, - value=(model_tipo.__name__, value), - label={'ind_mat_ou_doc': old.ind_mat_ou_doc}) + if old.tip_mat_ou_doc: + campo_virtual = CAMPOS_VIRTUAIS_TIPO_PROPOSICAO[old.ind_mat_ou_doc] + set_generic_fk(new, campo_virtual, old) def adjust_proposicao_antes_salvar(new, old): @@ -1098,8 +1101,7 @@ def adjust_proposicao_antes_salvar(new, old): if old.cod_mat_ou_doc: tipo_mat_ou_doc = type(new.tipo.tipo_conteudo_related) campo_virtual = CAMPOS_VIRTUAIS_PROPOSICAO[tipo_mat_ou_doc] - new.content_type = content_types[campo_virtual.related_model] - new.object_id = get_fk_related(campo_virtual, old) + set_generic_fk(new, campo_virtual, old) def adjust_statustramitacao(new, old): From dd8ac9203b728b20e12c0a3300ef97ea6f99b76f Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Tue, 10 Apr 2018 13:15:41 -0300 Subject: [PATCH 10/19] =?UTF-8?q?Executa=20ajustes=20pr=C3=A9-migra=C3=A7?= =?UTF-8?q?=C3=A3o=20se=20existirem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/legacy/migracao_dados.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py index f0bf710f0..9363e7464 100644 --- a/sapl/legacy/migracao_dados.py +++ b/sapl/legacy/migracao_dados.py @@ -782,6 +782,12 @@ def populate_renamed_fields(new, old): def migrar_dados(interativo=True): + # 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. From d7af0598eebaa8a6678f153ff1a5566738cccb6f Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Tue, 10 Apr 2018 14:25:21 -0300 Subject: [PATCH 11/19] Garante coluna no legado materia_legislativa.txt_resultado --- sapl/legacy/migracao_dados.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py index 9363e7464..52adfd2f1 100644 --- a/sapl/legacy/migracao_dados.py +++ b/sapl/legacy/migracao_dados.py @@ -567,6 +567,9 @@ def uniformiza_banco(): garante_coluna_no_legado('tipo_materia_legislativa', 'quorum_minimo_votacao int(11) NULL') + garante_coluna_no_legado('materia_legislativa', + 'txt_resultado TEXT NULL') + # Cria campos cod_presenca_sessao (sendo a nova PK da tabela) # e dat_sessao em sessao_plenaria_presenca if not existe_coluna_no_legado('sessao_plenaria_presenca', From 5370641ceb1cf5809ba7fa3df41079c9e7be0d4d Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Tue, 10 Apr 2018 15:06:39 -0300 Subject: [PATCH 12/19] Aumenta tamanho de numero_origem_externa em MateriaLegislativa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Algumas bases do sapl 2.5 têm esse campo com tamanho 9 --- sapl/legacy/models.py | 2 +- .../migrations/0028_auto_20180418_1629.py | 20 +++++++++++++++++++ sapl/materia/models.py | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 sapl/materia/migrations/0028_auto_20180418_1629.py diff --git a/sapl/legacy/models.py b/sapl/legacy/models.py index 4c459c9dd..7d341624d 100644 --- a/sapl/legacy/models.py +++ b/sapl/legacy/models.py @@ -433,7 +433,7 @@ class MateriaLegislativa(models.Model): cod_regime_tramitacao = models.IntegerField() dat_publicacao = models.DateField(blank=True, null=True) tip_origem_externa = models.IntegerField(blank=True, null=True) - num_origem_externa = models.CharField(max_length=5, blank=True, null=True) + num_origem_externa = models.CharField(max_length=10, blank=True, null=True) ano_origem_externa = models.SmallIntegerField(blank=True, null=True) dat_origem_externa = models.DateField(blank=True, null=True) cod_local_origem_externa = models.IntegerField(blank=True, null=True) diff --git a/sapl/materia/migrations/0028_auto_20180418_1629.py b/sapl/materia/migrations/0028_auto_20180418_1629.py new file mode 100644 index 000000000..c082ce1e6 --- /dev/null +++ b/sapl/materia/migrations/0028_auto_20180418_1629.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-04-18 19:29 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0027_auto_20180409_1443'), + ] + + operations = [ + migrations.AlterField( + model_name='materialegislativa', + name='numero_origem_externa', + field=models.CharField(blank=True, max_length=10, verbose_name='Número'), + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index ce4690c39..a1571072a 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -167,7 +167,7 @@ class MateriaLegislativa(models.Model): on_delete=models.PROTECT, verbose_name=_('Tipo')) numero_origem_externa = models.CharField( - max_length=5, blank=True, verbose_name=_('Número')) + max_length=10, blank=True, verbose_name=_('Número')) ano_origem_externa = models.PositiveSmallIntegerField( blank=True, null=True, verbose_name=_('Ano'), choices=RANGE_ANOS) data_origem_externa = models.DateField( From 0e8ff2e9871d8c855b53a0478c05cf7b3a0bc6a8 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Wed, 11 Apr 2018 15:43:52 -0300 Subject: [PATCH 13/19] Exporta docs do zope como repo git --- .../scripts/exporta_zope/exporta_zope.py | 29 ++++++++++++++++--- .../scripts/exporta_zope/requirements.txt | 2 ++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py index 11ff1ecbb..b3a4622b2 100755 --- a/sapl/legacy/scripts/exporta_zope/exporta_zope.py +++ b/sapl/legacy/scripts/exporta_zope/exporta_zope.py @@ -12,10 +12,12 @@ from collections import defaultdict from contextlib import contextmanager from functools import partial +import git import magic import yaml import ZODB.DB import ZODB.FileStorage +from unipath import Path from ZODB.broken import Broken EXTENSOES = { @@ -270,7 +272,7 @@ def dump_usuarios(sapl, path): save_as_yaml(path, 'usuarios.yaml', users) -def dump_sapl(data_fs_path, destino='../../../../media'): +def _dump_sapl(data_fs_path, destino='../../../../media'): app, close_db = get_app(data_fs_path) try: sapl = find_sapl(app) @@ -288,9 +290,28 @@ def dump_sapl(data_fs_path, destino='../../../../media'): close_db() +DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand() + + +def dump_sapl(sigla): + data_fs_path = DIR_DADOS_MIGRACAO.child('datafs', + 'Data_cm_{}.fs'.format(sigla)) + nome_banco_legado = 'sapl_cm_{}'.format(sigla) + destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado) + destino.mkdir(parents=True) + repo = git.Repo.init(destino) + assert not repo.index.diff(None) # o repo não tem mudanças pendentes + _dump_sapl(data_fs_path, destino) + # grava mundaças + repo.git.add(A=True) + if 'master' not in repo.heads or repo.index.diff('HEAD'): + # se de fato existe mudança + repo.index.commit('Exporta documentos do zope') + + if __name__ == "__main__": if len(sys.argv) == 2: - data_fs_path = sys.argv[1] - dump_sapl(data_fs_path) + sigla = sys.argv[1] + dump_sapl(sigla) else: - print('Uso: python exporta_zope ') + print('Uso: python exporta_zope ') diff --git a/sapl/legacy/scripts/exporta_zope/requirements.txt b/sapl/legacy/scripts/exporta_zope/requirements.txt index 4794267ae..421005181 100644 --- a/sapl/legacy/scripts/exporta_zope/requirements.txt +++ b/sapl/legacy/scripts/exporta_zope/requirements.txt @@ -1,3 +1,5 @@ # ZODB version 3.7.4 PyYAML==3.12 ZODB==5.3.0 +Unipath==1.1 +GitPython==2.1.9 From a9b8d4fbd42d283045b479ed0e2c28d3983bc0b8 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Wed, 11 Apr 2018 16:47:45 -0300 Subject: [PATCH 14/19] =?UTF-8?q?Adiciona=20tipo=20mp4=20=C3=A0=20exporta?= =?UTF-8?q?=C3=A7=C3=A3o=20de=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/legacy/scripts/exporta_zope/exporta_zope.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py index b3a4622b2..18a5bc3ef 100755 --- a/sapl/legacy/scripts/exporta_zope/exporta_zope.py +++ b/sapl/legacy/scripts/exporta_zope/exporta_zope.py @@ -40,6 +40,7 @@ EXTENSOES = { 'image/tiff': '.tiff', 'application/tiff': '.tiff', 'audio/x-wav': '.wav', + 'video/mp4': '.mp4', # TODO rever... 'text/richtext': '.rtf', @@ -273,6 +274,7 @@ def dump_usuarios(sapl, path): def _dump_sapl(data_fs_path, destino='../../../../media'): + assert Path(data_fs_path).exists() app, close_db = get_app(data_fs_path) try: sapl = find_sapl(app) @@ -296,6 +298,7 @@ DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand() def dump_sapl(sigla): data_fs_path = DIR_DADOS_MIGRACAO.child('datafs', 'Data_cm_{}.fs'.format(sigla)) + assert data_fs_path.exists() nome_banco_legado = 'sapl_cm_{}'.format(sigla) destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado) destino.mkdir(parents=True) From a1a5dda83b7b73ac75ca9e8a9c67f37f2999178e Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Thu, 12 Apr 2018 15:23:12 -0300 Subject: [PATCH 15/19] Grava nomes dos docs exportados --- .../scripts/exporta_zope/exporta_zope.py | 22 ++++++++++++++++++- .../scripts/exporta_zope/requirements.txt | 7 +++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py index 18a5bc3ef..19186a170 100755 --- a/sapl/legacy/scripts/exporta_zope/exporta_zope.py +++ b/sapl/legacy/scripts/exporta_zope/exporta_zope.py @@ -14,6 +14,7 @@ from functools import partial import git import magic +import pyaml import yaml import ZODB.DB import ZODB.FileStorage @@ -78,6 +79,9 @@ def guess_extension(caminho): raise Exception(msg, e) +nomes_arquivos_exportados = {} + + def dump_file(doc, path): name = doc['__name__'] fullname = os.path.join(path, name) @@ -112,6 +116,7 @@ def dump_file(doc, path): # trocamos a extensão pela adivinhada final_name = base + extension os.rename(fullname, final_name) + nomes_arquivos_exportados[fullname] = final_name print(final_name) return name @@ -273,9 +278,23 @@ def dump_usuarios(sapl, path): save_as_yaml(path, 'usuarios.yaml', users) +def grava_nomes_arquivos_exportados(destino): + """Grava nomes dos arquivos exportados (originais -> definitivos) + """ + destino = Path(destino) + + def rel(caminho): + return str(destino.rel_path_to(caminho)) + + with open(destino.child('arquivos_exportados.yaml'), 'w') as arq: + nomes = {rel(k): rel(v) for k, v in nomes_arquivos_exportados.items()} + pyaml.dump(nomes, arq) + + def _dump_sapl(data_fs_path, destino='../../../../media'): assert Path(data_fs_path).exists() app, close_db = get_app(data_fs_path) + nomes_arquivos_exportados.clear() # apaga nomes dos arquivos migrados try: sapl = find_sapl(app) # extrai folhas XSLT @@ -288,6 +307,7 @@ def _dump_sapl(data_fs_path, destino='../../../../media'): with logando_nao_identificados(): dump_folder(docs, destino) dump_propriedades(docs, destino) + grava_nomes_arquivos_exportados(destino) finally: close_db() @@ -298,7 +318,7 @@ DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand() def dump_sapl(sigla): data_fs_path = DIR_DADOS_MIGRACAO.child('datafs', 'Data_cm_{}.fs'.format(sigla)) - assert data_fs_path.exists() + assert data_fs_path.exists(), 'Origem não existe: {}'.format(data_fs_path) nome_banco_legado = 'sapl_cm_{}'.format(sigla) destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado) destino.mkdir(parents=True) diff --git a/sapl/legacy/scripts/exporta_zope/requirements.txt b/sapl/legacy/scripts/exporta_zope/requirements.txt index 421005181..c7aa86b4f 100644 --- a/sapl/legacy/scripts/exporta_zope/requirements.txt +++ b/sapl/legacy/scripts/exporta_zope/requirements.txt @@ -1,5 +1,6 @@ # ZODB version 3.7.4 -PyYAML==3.12 ZODB==5.3.0 -Unipath==1.1 -GitPython==2.1.9 +PyYAML +Unipath +GitPython +pyaml From d7e61137e0d72fe3d430369c2ffafa5b043b7c29 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Thu, 12 Apr 2018 19:31:48 -0300 Subject: [PATCH 16/19] =?UTF-8?q?Adiciona=20mais=20tipos=20de=20arquivo=20?= =?UTF-8?q?=C3=A0=20exporta=C3=A7=C3=A3o=20de=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/legacy/scripts/exporta_zope/exporta_zope.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py index 19186a170..3e7f20613 100755 --- a/sapl/legacy/scripts/exporta_zope/exporta_zope.py +++ b/sapl/legacy/scripts/exporta_zope/exporta_zope.py @@ -42,6 +42,8 @@ EXTENSOES = { 'application/tiff': '.tiff', 'audio/x-wav': '.wav', 'video/mp4': '.mp4', + 'image/x-icon': '.ico', + 'application/vnd.oasis.opendocument.text-template': '.ott', # TODO rever... 'text/richtext': '.rtf', From 1b26489baa191a59ef20f33401bfb17a88acf5ac Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Fri, 13 Apr 2018 10:55:28 -0300 Subject: [PATCH 17/19] =?UTF-8?q?Garante=20grava=C3=A7=C3=A3o=20de=20nomes?= =?UTF-8?q?=20de=20arqs=20exportados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/legacy/scripts/exporta_zope/exporta_zope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py index 3e7f20613..e27ea974d 100755 --- a/sapl/legacy/scripts/exporta_zope/exporta_zope.py +++ b/sapl/legacy/scripts/exporta_zope/exporta_zope.py @@ -309,9 +309,9 @@ def _dump_sapl(data_fs_path, destino='../../../../media'): with logando_nao_identificados(): dump_folder(docs, destino) dump_propriedades(docs, destino) - grava_nomes_arquivos_exportados(destino) finally: close_db() + grava_nomes_arquivos_exportados(destino) DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand() From d5bcea3ed9cfb588b24d19c3ef3d92e6433fc0e9 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Mon, 16 Apr 2018 16:17:26 -0300 Subject: [PATCH 18/19] Usa git annex --- sapl/legacy/scripts/exporta_zope/exporta_zope.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py index e27ea974d..ffcbc2d8c 100755 --- a/sapl/legacy/scripts/exporta_zope/exporta_zope.py +++ b/sapl/legacy/scripts/exporta_zope/exporta_zope.py @@ -317,6 +317,10 @@ def _dump_sapl(data_fs_path, destino='../../../../media'): DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand() +def repo_execute(repo, cmd): + return repo.git.execute(cmd.split()) + + def dump_sapl(sigla): data_fs_path = DIR_DADOS_MIGRACAO.child('datafs', 'Data_cm_{}.fs'.format(sigla)) @@ -325,9 +329,10 @@ def dump_sapl(sigla): destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado) destino.mkdir(parents=True) repo = git.Repo.init(destino) - assert not repo.index.diff(None) # o repo não tem mudanças pendentes _dump_sapl(data_fs_path, destino) # grava mundaças + repo_execute(repo, 'git annex init') + repo_execute(repo, 'git annex add sapl_documentos') repo.git.add(A=True) if 'master' not in repo.heads or repo.index.diff('HEAD'): # se de fato existe mudança From 87c7e82fd2ac434ea0f4031538cf45d4114770d9 Mon Sep 17 00:00:00 2001 From: Marcio Mazza Date: Wed, 18 Apr 2018 13:52:34 -0300 Subject: [PATCH 19/19] Salva apenas arquivos diferentes para o annex --- .../scripts/exporta_zope/exporta_zope.py | 150 +++++++++--------- 1 file changed, 79 insertions(+), 71 deletions(-) diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py index ffcbc2d8c..2c687bc07 100755 --- a/sapl/legacy/scripts/exporta_zope/exporta_zope.py +++ b/sapl/legacy/scripts/exporta_zope/exporta_zope.py @@ -5,6 +5,8 @@ # Esse script precisa rodar em python 2 # e depende apenas do descrito no arquivo requiments.txt +import cStringIO +import hashlib import mimetypes import os import sys @@ -16,9 +18,10 @@ import git import magic import pyaml import yaml +from unipath import Path + import ZODB.DB import ZODB.FileStorage -from unipath import Path from ZODB.broken import Broken EXTENSOES = { @@ -62,29 +65,25 @@ def br(obj): return obj -def guess_extension(caminho): - mime = magic.from_file(caminho, mime=True) +def guess_extension(fullname, buffer): + mime = magic.from_buffer(buffer, mime=True) try: return EXTENSOES[mime] except KeyError as e: - msg = '\n'.join([ - 'Extensão não conhecida para o arquivo:', - caminho, - 'E mimetype:', - mime, - ' Algumas possibilidades são:', ] + + possibilidades = '\n'.join( [" '{}': '{}',".format(mime, ext) - for ext in mimetypes.guess_all_extensions(mime)] + - ['Atualize o código do dicionário EXTENSOES!'] - ) + for ext in mimetypes.guess_all_extensions(mime)]) + msg = '''Extensão não conhecida para o arquivo: {} + e mimetype: {} + Algumas possibilidades são: + {} + Atualize o código do dicionário EXTENSOES! + '''.format(fullname, mime, possibilidades) print(msg) raise Exception(msg, e) -nomes_arquivos_exportados = {} - - -def dump_file(doc, path): +def dump_file(doc, path, salvar): name = doc['__name__'] fullname = os.path.join(path, name) @@ -104,23 +103,11 @@ def dump_file(doc, path): doc['data'] = pdata pdata = doc - with open(fullname, 'w') as arq: - while pdata: - arq.write(pdata.pop('data')) - pdata = br(pdata.pop('next', None)) - - base, original_extension = os.path.splitext(fullname) - extension = guess_extension(fullname) - if extension == '.xml' and original_extension in ['.xsl', '.xslt']: - # não trocamos as extensões XSL e XSLT - final_name = fullname - else: - # trocamos a extensão pela adivinhada - final_name = base + extension - os.rename(fullname, final_name) - nomes_arquivos_exportados[fullname] = final_name - print(final_name) - + output = cStringIO.StringIO() + while pdata: + output.write(pdata.pop('data')) + pdata = br(pdata.pop('next', None)) + salvar(fullname, output.getvalue()) return name @@ -165,7 +152,7 @@ def logando_nao_identificados(): print('#' * 80) -def dump_folder(folder, path='', enum=enumerate_folder): +def dump_folder(folder, path, salvar, enum=enumerate_folder): name = folder['id'] path = os.path.join(path, name) if not os.path.exists(path): @@ -175,7 +162,7 @@ def dump_folder(folder, path='', enum=enumerate_folder): if dump == '?': nao_identificados[meta_type].append(path + '/' + id) elif dump: - id_interno = dump(obj, path) + id_interno = dump(obj, path, salvar) assert id == id_interno return name @@ -211,18 +198,16 @@ def read_sde(element): return data -def save_as_yaml(path, name, obj): +def save_as_yaml(path, name, obj, salvar): fullname = os.path.join(path, name) - with open(fullname, 'w') as arquivo: - yaml.safe_dump(obj, arquivo, allow_unicode=True) - print(fullname) - return fullname + conteudo = yaml.safe_dump(obj, allow_unicode=True) + salvar(fullname, conteudo) -def dump_sde(strdoc, path, tipo): +def dump_sde(strdoc, path, salvar, tipo): id = strdoc['id'] sde = read_sde(strdoc) - save_as_yaml(path, '{}.{}.yaml'.format(id, tipo), sde) + save_as_yaml(path, '{}.{}.yaml'.format(id, tipo), sde, salvar) return id @@ -243,7 +228,7 @@ DUMP_FUNCTIONS = { def get_app(data_fs_path): - storage = ZODB.FileStorage.FileStorage(data_fs_path) + storage = ZODB.FileStorage.FileStorage(data_fs_path, read_only=True) db = ZODB.DB(storage) connection = db.open() root = connection.root() @@ -265,60 +250,79 @@ def find_sapl(app): return sapl -def dump_propriedades(docs, path, encoding='iso-8859-1'): +def dump_propriedades(docs, path, salvar, encoding='iso-8859-1'): 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()} - save_as_yaml(path, 'sapl_documentos/propriedades.yaml', props) + save_as_yaml(path, 'sapl_documentos/propriedades.yaml', props, salvar) -def dump_usuarios(sapl, path): +def dump_usuarios(sapl, path, salvar): users = br(br(sapl['acl_users'])['data']) users = {k: br(v) for k, v in users['data'].items()} - save_as_yaml(path, 'usuarios.yaml', users) - - -def grava_nomes_arquivos_exportados(destino): - """Grava nomes dos arquivos exportados (originais -> definitivos) - """ - destino = Path(destino) - - def rel(caminho): - return str(destino.rel_path_to(caminho)) - - with open(destino.child('arquivos_exportados.yaml'), 'w') as arq: - nomes = {rel(k): rel(v) for k, v in nomes_arquivos_exportados.items()} - pyaml.dump(nomes, arq) + save_as_yaml(path, 'usuarios.yaml', users, salvar) -def _dump_sapl(data_fs_path, destino='../../../../media'): +def _dump_sapl(data_fs_path, destino, salvar): assert Path(data_fs_path).exists() app, close_db = get_app(data_fs_path) - nomes_arquivos_exportados.clear() # apaga nomes dos arquivos migrados try: sapl = find_sapl(app) # extrai folhas XSLT - dump_folder(br(sapl['XSLT']), destino) + dump_folder(br(sapl['XSLT']), destino, salvar) # extrai usuários com suas senhas e perfis - dump_usuarios(sapl, destino) + dump_usuarios(sapl, destino, salvar) # extrai documentos docs = br(sapl['sapl_documentos']) with logando_nao_identificados(): - dump_folder(docs, destino) - dump_propriedades(docs, destino) + dump_folder(docs, destino, salvar) + dump_propriedades(docs, destino, salvar) finally: close_db() - grava_nomes_arquivos_exportados(destino) DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand() -def repo_execute(repo, cmd): - return repo.git.execute(cmd.split()) +def repo_execute(repo, cmd, *args): + return repo.git.execute(cmd.split() + list(args)) + + +def get_annex_hashes(repo): + hashes = repo_execute( + repo, 'git annex find', '--format=${keyname}\n', '--include=*') + return {os.path.splitext(h)[0] for h in hashes.splitlines()} + + +def ajusta_extensao(fullname, conteudo): + base, extensao = os.path.splitext(fullname) + if extensao not in ['.xsl', '.xslt', '.yaml']: + # não trocamos as extensões XSL, XSLT e YAML + extensao = guess_extension(fullname, conteudo) + return base + extensao + + +def build_salvar(repo): + """Constroi função salvar que pula arquivos que já estão no annex + """ + hashes = get_annex_hashes(repo) + + def salvar(fullname, conteudo): + sha = hashlib.sha256() + sha.update(conteudo) + if sha.hexdigest() not in hashes: + fullname = ajusta_extensao(fullname, conteudo) + if os.path.exists(fullname): + # destrava arquivo pré-existente (o conteúdo mudou) + repo_execute(repo, 'git annex unlock', fullname) + with open(fullname, 'w') as arq: + arq.write(conteudo) + print(fullname) + + return salvar def dump_sapl(sigla): @@ -329,9 +333,13 @@ def dump_sapl(sigla): destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado) destino.mkdir(parents=True) repo = git.Repo.init(destino) - _dump_sapl(data_fs_path, destino) - # grava mundaças repo_execute(repo, 'git annex init') + repo_execute(repo, 'git config annex.thin true') + + salvar = build_salvar(repo) + _dump_sapl(data_fs_path, destino, salvar) + + # grava mundaças repo_execute(repo, 'git annex add sapl_documentos') repo.git.add(A=True) if 'master' not in repo.heads or repo.index.diff('HEAD'):