mirror of https://github.com/interlegis/sapl.git
Marcio Mazza
7 years ago
3 changed files with 267 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||||
|
Data*.fs* |
||||
|
sapl_documentos |
||||
|
XSLT |
@ -0,0 +1,261 @@ |
|||||
|
#!/usr/bin/env python |
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
# IMPORTANTE: |
||||
|
# Esse script precisa rodar em python 2 |
||||
|
# e depende apenas do descrito no arquivo requiments.txt |
||||
|
|
||||
|
import os.path |
||||
|
import sys |
||||
|
from collections import defaultdict |
||||
|
from functools import partial |
||||
|
from os.path import splitext |
||||
|
|
||||
|
import yaml |
||||
|
import ZODB.DB |
||||
|
import ZODB.FileStorage |
||||
|
from ZODB.broken import Broken |
||||
|
|
||||
|
EXTENSOES = { |
||||
|
'application/msword': '.doc', |
||||
|
'application/pdf': '.pdf', |
||||
|
'application/vnd.oasis.opendocument.text': '.odt', |
||||
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx', # noqa |
||||
|
'application/xml': '.xml', |
||||
|
'text/xml': '.xml', |
||||
|
'application/zip': '.zip', |
||||
|
'image/jpeg': '.jpeg', |
||||
|
'image/png': '.png', |
||||
|
'image/gif': '.gif', |
||||
|
'text/html': '.html', |
||||
|
'text/rtf': '.rtf', |
||||
|
'text/x-python': '.py', |
||||
|
'text/plain': '.txt', |
||||
|
'SDE-Document': 'xml', |
||||
|
|
||||
|
# TODO rever... |
||||
|
'text/richtext': '.rtf', |
||||
|
|
||||
|
# sem extensao |
||||
|
'application/octet-stream': '', # binario |
||||
|
'inode/x-empty': '', # vazio |
||||
|
'text/x-unknown-content-type': '', |
||||
|
} |
||||
|
|
||||
|
|
||||
|
def br(obj): |
||||
|
if isinstance(obj, Broken): |
||||
|
return obj.__Broken_state__ |
||||
|
else: |
||||
|
return obj |
||||
|
|
||||
|
|
||||
|
extensoes_desconhecidas = defaultdict(list) |
||||
|
|
||||
|
|
||||
|
def dump_file(doc, path): |
||||
|
id = doc['__name__'] |
||||
|
name, extension = splitext(id) |
||||
|
content_type = doc['content_type'] |
||||
|
extension = extension or EXTENSOES.get(content_type, 'ZZZZ') |
||||
|
|
||||
|
fullname = os.path.join(path, name + extension) |
||||
|
print(fullname) |
||||
|
|
||||
|
if extension == 'ZZZZ': |
||||
|
extensoes_desconhecidas[content_type].append(fullname) |
||||
|
|
||||
|
# A partir daqui usamos dict.pop('...') nos __Broken_state__ |
||||
|
# para contornar um "vazamento" de memória que ocorre |
||||
|
# ao percorrer a árvore de objetos |
||||
|
# |
||||
|
# Imaginamos que, internamente, o ZODB está guardando referências |
||||
|
# para os objetos Broken criados e não conseguimos identificar como. |
||||
|
# |
||||
|
# Essa medida descarta quase todos os dados retornados |
||||
|
# e só funciona na primeira passagem |
||||
|
|
||||
|
pdata = br(doc.pop('data')) |
||||
|
if isinstance(pdata, str): |
||||
|
# Retrocedemos se pdata ja eh uma str (necessario em Images) |
||||
|
doc['data'] = pdata |
||||
|
pdata = doc |
||||
|
|
||||
|
with open(fullname, 'w') as arq: |
||||
|
while pdata: |
||||
|
arq.write(pdata.pop('data')) |
||||
|
pdata = br(pdata.pop('next', None)) |
||||
|
|
||||
|
return id |
||||
|
|
||||
|
|
||||
|
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 |
||||
|
|
||||
|
|
||||
|
enumerate_folder = partial(enumerate_by_key_list, |
||||
|
key_list='_objects', type_key='meta_type') |
||||
|
|
||||
|
enumerate_properties = partial(enumerate_by_key_list, |
||||
|
key_list='_properties', type_key='type') |
||||
|
|
||||
|
|
||||
|
def enumerate_btree(folder): |
||||
|
contagem_esperada = folder['_count'].value |
||||
|
tree = folder['_tree'] |
||||
|
for contagem_real, (id, obj) in enumerate(tree.iteritems(), start=1): |
||||
|
obj, meta_type = br(obj), type(obj).__name__ |
||||
|
yield id, obj, meta_type |
||||
|
# verificação de consistência |
||||
|
assert contagem_esperada == contagem_real |
||||
|
|
||||
|
|
||||
|
nao_identificados = defaultdict(list) |
||||
|
|
||||
|
|
||||
|
def dump_folder(folder, path='', enum=enumerate_folder): |
||||
|
name = folder['id'] |
||||
|
path = os.path.join(path, name) |
||||
|
if not os.path.exists(path): |
||||
|
os.makedirs(path) |
||||
|
for id, obj, meta_type in enum(folder): |
||||
|
dump = DUMP_FUNCTIONS.get(meta_type, '?') |
||||
|
if dump == '?': |
||||
|
nao_identificados[meta_type].append(path + '/' + id) |
||||
|
elif dump: |
||||
|
id_interno = dump(obj, path) |
||||
|
assert id == id_interno |
||||
|
return name |
||||
|
|
||||
|
|
||||
|
def decode_iso8859(obj): |
||||
|
return obj.decode('iso8859-1') if isinstance(obj, str) else obj |
||||
|
|
||||
|
|
||||
|
def read_sde(element): |
||||
|
|
||||
|
def read_properties(): |
||||
|
for id, obj, meta_type in enumerate_properties(element): |
||||
|
yield id, decode_iso8859(obj) |
||||
|
|
||||
|
def read_children(): |
||||
|
for id, obj, meta_type in enumerate_folder(element): |
||||
|
assert meta_type in ['SDE-Document-Element', |
||||
|
'SDE-Template-Element', |
||||
|
'SDE-Template-Link', |
||||
|
'SDE-Template-Attribute', |
||||
|
'Script (Python)', |
||||
|
] |
||||
|
if meta_type != 'Script (Python)': |
||||
|
# ignoramos os scrips python de eventos dos templates |
||||
|
yield id, read_sde(obj) |
||||
|
|
||||
|
data = dict(read_properties()) |
||||
|
children = list(read_children()) |
||||
|
if children: |
||||
|
data['children'] = children |
||||
|
return data |
||||
|
|
||||
|
|
||||
|
def save_as_yaml(path, name, obj): |
||||
|
fullname = os.path.join(path, name) |
||||
|
with open(fullname, 'w') as arquivo: |
||||
|
yaml.safe_dump(obj, arquivo) |
||||
|
print(fullname) |
||||
|
return fullname |
||||
|
|
||||
|
|
||||
|
def dump_sde(strdoc, path, tipo): |
||||
|
id = strdoc['id'] |
||||
|
sde = read_sde(strdoc) |
||||
|
save_as_yaml(path, '{}.{}.yaml'.format(id, tipo), sde) |
||||
|
return id |
||||
|
|
||||
|
|
||||
|
DUMP_FUNCTIONS = { |
||||
|
'File': dump_file, |
||||
|
'Image': dump_file, |
||||
|
'Folder': partial(dump_folder, enum=enumerate_folder), |
||||
|
'BTreeFolder2': partial(dump_folder, enum=enumerate_btree), |
||||
|
'SDE-Document': partial(dump_sde, tipo='sde.document'), |
||||
|
'SDE-Template': partial(dump_sde, tipo='sde.template'), |
||||
|
|
||||
|
# explicitamente ignorados |
||||
|
'ZCatalog': None, |
||||
|
'Dumper': None, |
||||
|
} |
||||
|
|
||||
|
|
||||
|
def get_app(data_fs_path): |
||||
|
storage = ZODB.FileStorage.FileStorage(data_fs_path) |
||||
|
db = ZODB.DB(storage) |
||||
|
connection = db.open() |
||||
|
root = connection.root() |
||||
|
app = br(root['Application']) |
||||
|
|
||||
|
def close_db(): |
||||
|
db.close() |
||||
|
|
||||
|
return app, close_db |
||||
|
|
||||
|
|
||||
|
def find_sapl(app): |
||||
|
for obj in app['_objects']: |
||||
|
id, meta_type = obj['id'], obj['meta_type'] |
||||
|
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 |
||||
|
|
||||
|
|
||||
|
def dump_propriedades(docs, path): |
||||
|
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('iso-8859-1') if isinstance(p, str) else p |
||||
|
for id, p in props.items()} |
||||
|
save_as_yaml(path, 'sapl_documentos/propriedades.yaml', props) |
||||
|
|
||||
|
|
||||
|
def dump_usuarios(sapl, path): |
||||
|
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 dump_sapl(data_fs_path, destino='../../../../media'): |
||||
|
app, close_db = get_app(data_fs_path) |
||||
|
try: |
||||
|
sapl = find_sapl(app) |
||||
|
# extrai folhas XSLT |
||||
|
dump_folder(br(sapl['XSLT']), destino) |
||||
|
# extrai usuários com suas senhas e perfis |
||||
|
dump_usuarios(sapl, destino) |
||||
|
|
||||
|
# extrai documentos |
||||
|
docs = br(sapl['sapl_documentos']) |
||||
|
nao_identificados.clear() |
||||
|
dump_folder(docs, destino) |
||||
|
dump_propriedades(docs, destino) |
||||
|
if nao_identificados: |
||||
|
print('#' * 80) |
||||
|
print('#' * 80) |
||||
|
print(u'FORAM ENCONTRADOS ARQUIVOS DE FORMATO NÃO IDENTIFICADO!!!') |
||||
|
print(u'REFAÇA A EXPORTAÇÃO\n') |
||||
|
print(nao_identificados) |
||||
|
print('#' * 80) |
||||
|
print('#' * 80) |
||||
|
finally: |
||||
|
close_db() |
||||
|
|
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
if len(sys.argv) == 2: |
||||
|
data_fs_path = sys.argv[1] |
||||
|
dump_sapl(data_fs_path) |
||||
|
else: |
||||
|
print('Uso: python exporta_zope <caminho p Data.fs>') |
@ -0,0 +1,3 @@ |
|||||
|
# ZODB version 3.7.4 |
||||
|
PyYAML==3.12 |
||||
|
ZODB==5.3.0 |
Loading…
Reference in new issue