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/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) 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_dados.py b/sapl/legacy/migracao_dados.py index 9b2846fa7..52adfd2f1 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 @@ -8,6 +9,7 @@ from operator import xor from subprocess import PIPE, call import pkg_resources +import pyaml import pytz import reversion import yaml @@ -18,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 @@ -143,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)} @@ -201,7 +213,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 +506,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 +532,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'), @@ -548,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', @@ -763,6 +785,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. @@ -800,8 +828,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) - 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 @@ -816,7 +843,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 @@ -829,7 +856,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) @@ -1061,22 +1092,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): @@ -1085,8 +1110,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): @@ -1226,4 +1250,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) 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)) 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/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 diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py index 11ff1ecbb..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 @@ -12,8 +14,12 @@ from collections import defaultdict from contextlib import contextmanager from functools import partial +import git import magic +import pyaml import yaml +from unipath import Path + import ZODB.DB import ZODB.FileStorage from ZODB.broken import Broken @@ -38,6 +44,9 @@ EXTENSOES = { 'image/tiff': '.tiff', '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', @@ -56,26 +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) -def dump_file(doc, path): +def dump_file(doc, path, salvar): name = doc['__name__'] fullname = os.path.join(path, name) @@ -95,22 +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) - 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 @@ -155,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): @@ -165,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 @@ -201,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 @@ -233,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() @@ -255,42 +250,106 @@ 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) + 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) 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() +DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand() + + +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): + data_fs_path = DIR_DADOS_MIGRACAO.child('datafs', + 'Data_cm_{}.fs'.format(sigla)) + 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) + repo = git.Repo.init(destino) + 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'): + # 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..c7aa86b4f 100644 --- a/sapl/legacy/scripts/exporta_zope/requirements.txt +++ b/sapl/legacy/scripts/exporta_zope/requirements.txt @@ -1,3 +1,6 @@ # ZODB version 3.7.4 -PyYAML==3.12 ZODB==5.3.0 +PyYAML +Unipath +GitPython +pyaml 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 + 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'] 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(