diff --git a/requirements/migration-requirements.txt b/requirements/migration-requirements.txt index fbe9543e2..e3f887b14 100644 --- a/requirements/migration-requirements.txt +++ b/requirements/migration-requirements.txt @@ -1,3 +1,4 @@ -r dev-requirements.txt +GitPython mysqlclient==1.3.12 pyaml diff --git a/sapl/legacy/migracao.py b/sapl/legacy/migracao.py index be8830aa8..a72bd9aaa 100644 --- a/sapl/legacy/migracao.py +++ b/sapl/legacy/migracao.py @@ -3,15 +3,16 @@ import tarfile from django.conf import settings -from sapl.legacy.migracao_dados import migrar_dados +from sapl.legacy.migracao_dados import REPO, gravar_marco, migrar_dados from sapl.legacy.migracao_documentos import migrar_documentos from sapl.legacy.migracao_usuarios import migrar_usuarios def migrar(interativo=False): migrar_dados(interativo=interativo) - migrar_usuarios() - migrar_documentos() + migrar_usuarios(REPO.working_dir) + migrar_documentos(REPO) + gravar_marco() def gerar_pacote(): diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py index 52adfd2f1..492a8e579 100644 --- a/sapl/legacy/migracao_dados.py +++ b/sapl/legacy/migracao_dados.py @@ -8,6 +8,7 @@ from itertools import groupby from operator import xor from subprocess import PIPE, call +import git import pkg_resources import pyaml import pytz @@ -1256,30 +1257,44 @@ AJUSTE_DEPOIS_SALVAR = { TIME_FORMAT = '%H:%M:%S' +# permite a gravação de tempos puros pelo pretty-yaml def time_representer(dumper, data): return dumper.represent_scalar('!time', data.strftime(TIME_FORMAT)) UnsafePrettyYAMLDumper.add_representer(datetime.time, time_representer) +# permite a leitura de tempos puros pelo pyyaml (no padrão gravado acima) 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) +REPO = git.Repo.init(Path(DIR_DADOS_MIGRACAO, 'repos', nome_banco_legado)) -def grava_marco_base(): +def gravar_marco(): + """Grava um dump de todos os dados como arquivos yaml no repo de marco + """ + # prepara ou localiza repositorio + dir_dados = Path(REPO.working_dir, 'dados') + + # exporta dados como arquivos yaml 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 = dir_dados.child(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']) + nome_arq = Path(dir_model, '{}.yaml'.format(data['id'])) with open(nome_arq, 'w') as arq: pyaml.dump(data, arq) + + # salva mudanças + REPO.git.add([dir_dados.name]) + if 'master' not in REPO.heads or REPO.index.diff('HEAD'): + # se de fato existe mudança + REPO.index.commit('Grava marco') + REPO.git.execute('git tag marco'.split()) diff --git a/sapl/legacy/migracao_documentos.py b/sapl/legacy/migracao_documentos.py index a94b9160a..57446f39c 100644 --- a/sapl/legacy/migracao_documentos.py +++ b/sapl/legacy/migracao_documentos.py @@ -1,6 +1,7 @@ import os import re from glob import glob +from os.path import join import yaml from django.db import transaction @@ -14,7 +15,6 @@ from sapl.parlamentares.models import Parlamentar from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo) from sapl.sessao.models import SessaoPlenaria -from sapl.settings import MEDIA_ROOT # MIGRAÇÃO DE DOCUMENTOS ################################################### @@ -34,25 +34,41 @@ DOCS = { DocumentoAcessorioAdministrativo: [('arquivo', 'administrativo/{}')], } -DOCS = {model: [(campo, os.path.join('sapl_documentos', origem)) +DOCS = {model: [(campo, join('sapl_documentos', origem)) for campo, origem, in campos] for model, campos in DOCS.items()} -def em_media(caminho): - return os.path.join(MEDIA_ROOT, caminho) - - -def mover_documento(origem, destino): - origem, destino = [em_media(c) if not os.path.isabs(c) else c +def mover_documento(repo, origem, destino): + origem, destino = [join(repo.working_dir, c) if not os.path.isabs(c) else c for c in (origem, destino)] os.makedirs(os.path.dirname(destino), exist_ok=True) - os.rename(origem, destino) + repo.git.mv(origem, destino) + # conserta link do git annex (antes do commit) + # em geral é o mais seguro a fazer, + # mas foi especificamente necessário pois o conteúdo das imagens + # é acessado antes do commit pelo cropping de imagem + repo.git.execute('git annex fix'.split() + [destino]) -def migrar_propriedades_da_casa(): +def migrar_logotipo(repo, casa, propriedades): + print('.... Migrando logotipo da casa ....') + [(campo, origem)] = DOCS[CasaLegislativa] + # 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))) + if arquivos: + assert len(arquivos) == 1, 'Há mais de um logotipo para a casa' + [logo] = arquivos + destino = join(CasaLegislativa._meta.get_field(campo).upload_to, + os.path.basename(logo)) + mover_documento(repo, logo, destino) + casa.logotipo = destino + + +def migrar_propriedades_da_casa(repo): print('#### Migrando propriedades da casa ####') - caminho = em_media('sapl_documentos/propriedades.yaml') + caminho = join(repo.working_dir, 'sapl_documentos/propriedades.yaml') with open(caminho, 'r') as arquivo: propriedades = yaml.safe_load(arquivo) casa = CasaLegislativa.objects.first() @@ -72,31 +88,24 @@ def migrar_propriedades_da_casa(): for campo, prop in campos_para_propriedades: setattr(casa, campo, propriedades[prop]) - # Localidade + # localidade sql_localidade = ''' select nom_localidade, sgl_uf from localidade where cod_localidade = {}'''.format(propriedades['cod_localidade']) [(casa.municipio, casa.uf)] = exec_legado(sql_localidade) - print('.... Migrando logotipo da casa ....') - [(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( - CasaLegislativa._meta.get_field(campo).upload_to, - os.path.basename(origem)) - mover_documento(origem, destino) - casa.logotipo = destino + # logotipo + migrar_logotipo(repo, casa, propriedades) + casa.save() - os.remove(caminho) + repo.git.rm(caminho) -def migrar_docs_por_ids(model): +def migrar_docs_por_ids(repo, 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)) + dir_origem, nome_origem = os.path.split(join(repo.working_dir, base_origem)) nome_origem = nome_origem.format('(\d+)') pat = re.compile('^{}\.\w+$'.format(nome_origem)) if not os.path.isdir(dir_origem): @@ -106,7 +115,7 @@ def migrar_docs_por_ids(model): 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))) + 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 @@ -117,7 +126,7 @@ def migrar_docs_por_ids(model): obj = objetos.get(id) if obj: destino = upload_to(obj, os.path.basename(origem)) - mover_documento(origem, destino) + mover_documento(repo, origem, destino) setattr(obj, campo, destino) obj.save() else: @@ -125,16 +134,15 @@ def migrar_docs_por_ids(model): print(msg.format(model.__name__, id, origem)) -def migrar_documentos(): - # aqui supomos que uma pasta chamada sapl_documentos está em MEDIA_ROOT - # com o conteúdo da pasta de mesmo nome do zope - # Os arquivos da pasta serão MOVIDOS para a nova estrutura! - # A pasta, após conferência do que não foi migrado, deve ser apagada. +def migrar_documentos(repo): + # aqui supomos que uma pasta chamada sapl_documentos está em + # com o conteúdo exportado do zope + # Os arquivos da pasta serão (git) MOVIDOS para a nova estrutura! # # Isto significa que para rodar novamente esta função é preciso # restaurar a pasta sapl_documentos ao estado inicial - migrar_propriedades_da_casa() + migrar_propriedades_da_casa(repo) for model in [ Parlamentar, @@ -146,10 +154,11 @@ def migrar_documentos(): DocumentoAdministrativo, DocumentoAcessorioAdministrativo, ]: - migrar_docs_por_ids(model) + migrar_docs_por_ids(repo, model) - sobrando = [os.path.join(dir, file) - for (dir, _, files) in os.walk(em_media('sapl_documentos')) + sobrando = [join(dir, file) + for (dir, _, files) in os.walk(join(repo.working_dir, + 'sapl_documentos')) for file in files] if sobrando: print('\n#### Encerrado ####\n\n' diff --git a/sapl/legacy/migracao_usuarios.py b/sapl/legacy/migracao_usuarios.py index 106a2def6..be0478c82 100644 --- a/sapl/legacy/migracao_usuarios.py +++ b/sapl/legacy/migracao_usuarios.py @@ -1,8 +1,8 @@ import yaml from django.contrib.auth.models import Group, User +from unipath import Path from sapl.hashers import zope_encoded_password_to_django -from sapl.settings import MEDIA_ROOT PERFIL_LEGADO_PARA_NOVO = {legado: Group.objects.get(name=novo) for legado, novo in [ @@ -44,9 +44,9 @@ def decode_nome(nome): return nome -def migrar_usuarios(): +def migrar_usuarios(dir_repo): """ - Lê o arquivo media/usuarios.yaml e importa os usuários nele listados, + Lê o arquivo /usuarios.yaml e importa os usuários nele listados, com senhas e perfis. Os usuários são criados se necessário e seus perfis ajustados. @@ -68,7 +68,7 @@ def migrar_usuarios(): Também podemos assumir que essa é uma tarefa de um administrador """ - ARQUIVO_USUARIOS = MEDIA_ROOT.child('usuarios.yaml') + ARQUIVO_USUARIOS = Path(dir_repo).child('usuarios.yaml') with open(ARQUIVO_USUARIOS, 'r') as f: usuarios = yaml.load(f) # conferimos de que só há um nome de usuário