mirror of https://github.com/interlegis/sapl.git
Edward
6 years ago
committed by
GitHub
5 changed files with 303 additions and 2 deletions
@ -0,0 +1,285 @@ |
|||||
|
from datetime import datetime |
||||
|
|
||||
|
import oaipmh |
||||
|
import oaipmh.error |
||||
|
import oaipmh.metadata |
||||
|
import oaipmh.server |
||||
|
from django.urls import reverse |
||||
|
from lxml import etree |
||||
|
from lxml.builder import ElementMaker |
||||
|
|
||||
|
from sapl.base.models import AppConfig, CasaLegislativa |
||||
|
from sapl.lexml.models import LexmlPublicador |
||||
|
from sapl.norma.models import NormaJuridica |
||||
|
|
||||
|
|
||||
|
class OAILEXML: |
||||
|
""" |
||||
|
Padrao OAI do LeXML |
||||
|
Esta registrado sobre o nome 'oai_lexml' |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, prefix): |
||||
|
self.prefix = prefix |
||||
|
self.ns = {'oai_lexml': 'http://www.lexml.gov.br/oai_lexml', } |
||||
|
self.schemas = {'oai_lexml': 'http://projeto.lexml.gov.br/esquemas/oai_lexml.xsd'} |
||||
|
|
||||
|
def __call__(self, element, metadata): |
||||
|
data = metadata.record |
||||
|
value = etree.XML(data['metadata']) |
||||
|
element.append(value) |
||||
|
|
||||
|
|
||||
|
class OAIServer: |
||||
|
""" |
||||
|
An OAI-2.0 compliant oai server. |
||||
|
Underlying code is based on pyoai's oaipmh.server' |
||||
|
""" |
||||
|
|
||||
|
XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance' |
||||
|
ns = {'lexml': 'http://www.lexml.gov.br/oai_lexml'} |
||||
|
schema = {'oai_lexml': 'http://projeto.lexml.gov.br/esquemas/oai_lexml.xsd'} |
||||
|
|
||||
|
def __init__(self, config={}): |
||||
|
self.config = config |
||||
|
|
||||
|
def identify(self): |
||||
|
result = oaipmh.common.Identify( |
||||
|
repositoryName=self.config['titulo'], |
||||
|
baseURL=self.config['base_url'], |
||||
|
protocolVersion='2.0', |
||||
|
adminEmails=self.config['email'], |
||||
|
earliestDatestamp=datetime(2001, 1, 1, 10, 00), |
||||
|
deletedRecord='transient', |
||||
|
granularity='YYYY-MM-DDThh:mm:ssZ', |
||||
|
compression=['identity'], |
||||
|
toolkit_description=False) |
||||
|
if not self.config['descricao']: |
||||
|
result.add_description(self.config['descricao']) |
||||
|
return result |
||||
|
|
||||
|
def create_header_and_metadata(self, record): |
||||
|
header = self.create_header(record) |
||||
|
metadata = oaipmh.common.Metadata(None, record['metadata']) |
||||
|
metadata.record = record |
||||
|
return header, metadata |
||||
|
|
||||
|
def list_query(self, from_date=None, until_date=None, offset=0, batch_size=10, identifier=None): |
||||
|
if identifier: |
||||
|
identifier = int(identifier.split('/')[-1]) # Get internal id |
||||
|
else: |
||||
|
identifier = '' |
||||
|
until_date = datetime.now() if not until_date or until_date > datetime.now() else until_date |
||||
|
return self.oai_query(offset=offset, batch_size=batch_size, from_date=from_date, until_date=until_date, |
||||
|
identifier=identifier) |
||||
|
|
||||
|
def check_metadata_prefix(self, metadata_prefix): |
||||
|
if not metadata_prefix in self.config['metadata_prefixes']: |
||||
|
raise oaipmh.error.CannotDisseminateFormatError |
||||
|
|
||||
|
def listRecords(self, metadataPrefix, from_date=None, until_date=None, cursor=0, batch_size=10): |
||||
|
self.check_metadata_prefix(metadataPrefix) |
||||
|
for record in self.list_query(from_date, until_date, cursor, batch_size): |
||||
|
header, metadata = self.create_header_and_metadata(record) |
||||
|
yield header, metadata, None # None? |
||||
|
|
||||
|
def get_oai_id(self, internal_id): |
||||
|
return "oai:{}".format(internal_id) |
||||
|
|
||||
|
def create_header(self, record): |
||||
|
oai_id = self.get_oai_id(record['record']['id']) |
||||
|
timestamp = record['record']['when_modified'] |
||||
|
timestamp = timestamp.replace(tzinfo=None) |
||||
|
sets = [] |
||||
|
deleted = record['record']['deleted'] |
||||
|
return oaipmh.common.Header(None, oai_id, timestamp, sets, deleted) |
||||
|
|
||||
|
def get_esfera_federacao(self): |
||||
|
appconfig = AppConfig.objects.first() |
||||
|
return appconfig.esfera_federacao |
||||
|
|
||||
|
def recupera_norma(self, offset, batch_size, from_date, until_date, identifier, esfera): |
||||
|
kwargs = {'data__lte': until_date} |
||||
|
if from_date: |
||||
|
kwargs['data__gte'] = from_date |
||||
|
if identifier: |
||||
|
kwargs['numero'] = identifier |
||||
|
if esfera: |
||||
|
kwargs['esfera_federacao'] = esfera |
||||
|
return NormaJuridica.objects.select_related('tipo').filter(**kwargs)[offset:offset + batch_size] |
||||
|
|
||||
|
def monta_id(self, norma): |
||||
|
if norma: |
||||
|
num = len(casa.endereco_web.split('.')) |
||||
|
dominio = '.'.join(casa.endereco_web.split('.')[1:num]) |
||||
|
prefixo_oai = '{}.{}:sapl/'.format(casa.sigla.lower(), dominio) |
||||
|
numero_interno = norma.numero |
||||
|
tipo_norma = norma.tipo.equivalente_lexml |
||||
|
ano_norma = norma.ano |
||||
|
identificador = '{}{};{};{}'.format(prefixo_oai, tipo_norma, ano_norma, numero_interno) |
||||
|
return identificador |
||||
|
else: |
||||
|
return None |
||||
|
|
||||
|
def monta_urn(self, norma, esfera): |
||||
|
if norma: |
||||
|
urn = 'urn:lex:br;' |
||||
|
esferas = {'M': 'municipal', 'E': 'estadual'} |
||||
|
municipio = casa.municipio.lower() |
||||
|
uf = casa.uf.lower() |
||||
|
for x in [' ', '.de.', '.da.', '.das.', '.do.', '.dos.']: |
||||
|
municipio = municipio.replace(x, '.') |
||||
|
uf = uf.replace(x, '.') |
||||
|
if esfera == 'M': |
||||
|
urn += '{};{}:'.format(uf, municipio) |
||||
|
if norma.tipo.equivalente_lexml == 'regimento.interno' or norma.tipo.equivalente_lexml == 'resolucao': |
||||
|
urn += 'camara.' |
||||
|
urn += esferas[esfera] + ':' |
||||
|
elif esfera == 'E': |
||||
|
urn += '{}:{}:'.format(uf, esferas[esfera]) |
||||
|
else: |
||||
|
urn += ':' |
||||
|
if norma.tipo.equivalente_lexml: |
||||
|
urn += '{}:{};'.format(norma.tipo.equivalente_lexml, norma.data.isoformat()) |
||||
|
else: |
||||
|
urn += '{};'.format(norma.data.isoformat()) |
||||
|
if norma.tipo.equivalente_lexml == 'lei.organica' or norma.tipo.equivalente_lexml == 'constituicao': |
||||
|
urn += norma.ano |
||||
|
else: |
||||
|
urn += norma.numero |
||||
|
if norma.data_vigencia and norma.data_publicacao: |
||||
|
urn += '@{};publicacao;{}'.format(norma.data_vigencia.isoformat(), norma.data_publicacao.isoformat()) |
||||
|
elif norma.data_publicacao: |
||||
|
urn += '@inicio.vigencia;publicacao;{}'.format(norma.data_publicacao.isoformat()) |
||||
|
return urn |
||||
|
else: |
||||
|
return None |
||||
|
|
||||
|
def data_por_extenso(self, data): |
||||
|
data = data.strftime('%d-%m-%Y') |
||||
|
if data != '': |
||||
|
meses = {1: 'Janeiro', 2: 'Fevereiro', 3: 'Março', 4: 'Abril', 5: 'Maio', 6: 'Junho', 7: 'Julho', |
||||
|
8: 'Agosto', 9: 'Setembro', 10: 'Outubro', 11: 'Novembro', 12: 'Dezembro'} |
||||
|
return '{} de {} de {}'.format(data[0:2], meses[int(data[3:5])], data[6:]) |
||||
|
else: |
||||
|
return '' |
||||
|
|
||||
|
def monta_xml(self, urn, norma): |
||||
|
publicador = LexmlPublicador.objects.first() |
||||
|
if norma and publicador: |
||||
|
LEXML = ElementMaker(namespace=self.ns['lexml'], nsmap=self.ns) |
||||
|
oai_lexml = LEXML.LexML() |
||||
|
oai_lexml.attrib['{{}}schemaLocation'.format(self.XSI_NS)] = '{} {}'.format( |
||||
|
'http://www.lexml.gov.br/oai_lexml', 'http://projeto.lexml.gov.br/esquemas/oai_lexml.xsd') |
||||
|
texto_integral = norma.texto_integral |
||||
|
mime_types = {'doc': 'application/msword', |
||||
|
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
||||
|
'odt': 'application/vnd.oasis.opendocument.text', |
||||
|
'pdf': 'application/pdf', |
||||
|
'rtf': 'application/rtf'} |
||||
|
if texto_integral: |
||||
|
url_conteudo = self.config['base_url'] + texto_integral.url |
||||
|
extensao = texto_integral.url.split('.')[-1] |
||||
|
formato = mime_types.get(extensao, 'application/octet-stream') |
||||
|
else: |
||||
|
formato = 'text/html' |
||||
|
url_conteudo = self.config['base_url'] + reverse('sapl.norma:normajuridica_detail', |
||||
|
kwargs={'pk': norma.numero}) |
||||
|
element_maker = ElementMaker() |
||||
|
id_publicador = str(publicador.id_publicador) |
||||
|
item_conteudo = element_maker.Item(url_conteudo, formato=formato, idPublicador=id_publicador, |
||||
|
tipo='conteudo') |
||||
|
oai_lexml.append(item_conteudo) |
||||
|
url = self.config['base_url'] + reverse('sapl.norma:normajuridica_detail', kwargs={'pk': norma.numero}) |
||||
|
item_metadado = element_maker.Item(url, formato='text/html', idPublicador=id_publicador, tipo='metadado') |
||||
|
oai_lexml.append(item_metadado) |
||||
|
documento_individual = element_maker.DocumentoIndividual(urn) |
||||
|
oai_lexml.append(documento_individual) |
||||
|
if norma.tipo.equivalente_lexml == 'lei.organica': |
||||
|
epigrafe = '{} de {} - {}, de {}'.format(norma.tipo.descricao, casa.municipio, |
||||
|
casa.uf, norma.ano) |
||||
|
elif norma.tipo.equivalente_lexml == 'constituicao': |
||||
|
epigrafe = '{} do Estado de {}, de {}'.format(norma.tipo.descricao, casa.municipio, |
||||
|
norma.ano) |
||||
|
else: |
||||
|
epigrafe = '{} n° {}, de {}'.format(norma.tipo.descricao, norma.numero, |
||||
|
self.data_por_extenso(norma.data)) |
||||
|
oai_lexml.append(element_maker.Epigrafe(epigrafe)) |
||||
|
oai_lexml.append(element_maker.Ementa(norma.ementa)) |
||||
|
indexacao = norma.indexacao |
||||
|
if indexacao: |
||||
|
oai_lexml.append(element_maker.Indexacao(indexacao)) |
||||
|
return etree.tostring(oai_lexml) |
||||
|
else: |
||||
|
return None |
||||
|
|
||||
|
def oai_query(self, offset=0, batch_size=10, from_date=None, until_date=None, identifier=None): |
||||
|
esfera = self.get_esfera_federacao() |
||||
|
offset = 0 if offset < 0 else offset |
||||
|
batch_size = 10 if batch_size < 0 else batch_size |
||||
|
until_date = datetime.now() if not until_date or until_date > datetime.now() else until_date |
||||
|
normas = self.recupera_norma(offset, batch_size, from_date, until_date, identifier, esfera) |
||||
|
for norma in normas: |
||||
|
resultado = {} |
||||
|
identificador = self.monta_id(norma) |
||||
|
urn = self.monta_urn(norma, esfera) |
||||
|
xml_lexml = self.monta_xml(urn, norma) |
||||
|
resultado['tx_metadado_xml'] = xml_lexml |
||||
|
resultado['cd_status'] = 'N' |
||||
|
resultado['id'] = identificador |
||||
|
resultado['when_modified'] = norma.timestamp |
||||
|
resultado['deleted'] = 0 |
||||
|
yield {'record': resultado, |
||||
|
'metadata': resultado['tx_metadado_xml']} |
||||
|
|
||||
|
|
||||
|
def OAIServerFactory(config={}): |
||||
|
""" |
||||
|
Create a new OAI batching OAI Server given a config and a database |
||||
|
""" |
||||
|
for prefix in config['metadata_prefixes']: |
||||
|
metadata_registry = oaipmh.metadata.MetadataRegistry() |
||||
|
metadata_registry.registerWriter(prefix, OAILEXML(prefix)) |
||||
|
return oaipmh.server.BatchingServer( |
||||
|
OAIServer(config), |
||||
|
metadata_registry=metadata_registry, |
||||
|
resumption_batch_size=config['batch_size'] |
||||
|
) |
||||
|
|
||||
|
|
||||
|
casa = None |
||||
|
|
||||
|
|
||||
|
def casa_legislativa(): |
||||
|
global casa |
||||
|
if not casa: |
||||
|
casa = CasaLegislativa.objects.first() |
||||
|
return casa if casa else CasaLegislativa() # retorna objeto dummy |
||||
|
|
||||
|
|
||||
|
def get_config(url, batch_size): |
||||
|
config = {'content_type': None, |
||||
|
'delay': 0, |
||||
|
'base_asset_path': None, |
||||
|
'metadata_prefixes': ['oai_lexml']} |
||||
|
config.update({'titulo': casa_legislativa().nome, # Inicializa variável global casa |
||||
|
'email': casa.email, |
||||
|
'base_url': url[:url.find('/', 8)], |
||||
|
'descricao': casa.informacao_geral, |
||||
|
'batch_size': batch_size}) |
||||
|
return config |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
""" |
||||
|
Para executar localmente (estando no diretório raiz): |
||||
|
|
||||
|
$ ./manage.py shell_plus |
||||
|
|
||||
|
Executar comando |
||||
|
%run sapl/lexml/OAIServer.py |
||||
|
""" |
||||
|
oai_server = OAIServerFactory(get_config('http://127.0.0.1:8000/', 10)) |
||||
|
r = oai_server.handleRequest({'verb': 'ListRecords', |
||||
|
'metadataPrefix': 'oai_lexml'}) |
||||
|
print(r.decode('UTF-8')) |
@ -1,6 +1,18 @@ |
|||||
|
from django.http import HttpResponse |
||||
|
|
||||
from sapl.crud.base import CrudAux |
from sapl.crud.base import CrudAux |
||||
|
from sapl.lexml.OAIServer import OAIServerFactory, get_config |
||||
|
|
||||
from .models import LexmlProvedor, LexmlPublicador |
from .models import LexmlProvedor, LexmlPublicador |
||||
|
|
||||
LexmlProvedorCrud = CrudAux.build(LexmlProvedor, 'lexml_provedor') |
LexmlProvedorCrud = CrudAux.build(LexmlProvedor, 'lexml_provedor') |
||||
LexmlPublicadorCrud = CrudAux.build(LexmlPublicador, 'lexml_publicador') |
LexmlPublicadorCrud = CrudAux.build(LexmlPublicador, 'lexml_publicador') |
||||
|
|
||||
|
|
||||
|
def lexml_request(request): |
||||
|
config = get_config(request.get_raw_uri(), int(request.GET.get('batch_size', 10))) |
||||
|
oai_server = OAIServerFactory(config) |
||||
|
r = oai_server.handleRequest({'verb': request.GET.get('verb', 'ListRecords'), |
||||
|
'metadataPrefix': request.GET.get('metadataPrefix', 'oai_lexml')}) |
||||
|
response = r.decode('UTF-8') |
||||
|
return HttpResponse(response, content_type='text/xml') |
||||
|
Loading…
Reference in new issue