Django-Fossil no OpenTrials

O django-fossil foi criado para atender primariamente ao OpenTrials, mas tomamos os cuidados para deixá-lo agnóstico a outros projetos.

repository.serializers

O núcleo do uso do django-fossil no OpenTrials está no modulo repository.serializers, onde estão as funções de serialização e deserialização chamadas nas classes de modelo e managers do modulo repository.models. Apesar de estarem prontas, as funções deserialize_ não são usadas efetivamente no OpenTrials devido às características do projeto, pois a deserialização que importa é feita para XML, com validação via DTD, etc. Outro motivo para isso é que no OpenTrials existe apenas um registro principal serializado, que é o ensaio clínico (classe repository.ClinicalTrial?) e nele estão contidos todos os objetos dos quais ele depende. Isso tem um pequeno conflito com a ideia de deserialização do django-fossil, que trabalha separado por entidade.

repository.xml

Portanto, o outro pacote importante nesse processo é o repository.xml, especialmente os pacotes validate, generate e import.

respository.xml.validate

Contém o método validate_xml(filename_or_xmltree, dtd='opentrials.dtd'), responsável pela validação, usando DTD.

respository.xml.generate

Contém funções para geração do XML de ensaios clínicos nos formatos WHO ICTRP xml_ictrp(trial) e OpenTrialsXML xml_opentrials(trial, persons, include_translations=True). Tem ainda a função xml_opentrials_mod() para gerar o arquivo de vocabulários opentrials-vocabularies.mod

respository.xml.loading

Contém a classe OpenTrialsXMLImport, que é usada tanto para a importação de arquivos no formato WHO ICTRP quanto OpenTrials XML. A lógica de funcionamento dessa classe segue a seguinte ordem:

1. Criação da instância, recebendo o usuário creator desta importação. Ele será atribuído como criador para novos objetos de Contact ou Institution.

Exemplo:

from repository.xml.loading import OpenTrialsXMLImport
imp = OpenTrialsXMLImport(creator=request.user)

2. Carregamento de uma instância de etree - classe raiz da biblioteca lxml - contendo o XML a importar.

Exemplo:

from repository.xml.loading import etree
tree = etree.parse('caminho_do_arquivo.xml')

3. Validação do etree, usando a função repository.xml.validate.validate_xml.

Exemplo:

from repository.xml.validate import validate_xml
from repository.xml.validate import InvalidOpenTrialsXML
from repository.xml.validate import ICTRP_DTD, DEFAULT_DTD

try:
    validate_xml(tree)
    xml_format = 'opentrials'
except InvalidOpenTrialsXML:
    try:
        validate_xml(tree, dtd=ICTRP_DTD)
       xml_format = 'ictrp'
    except InvalidOpenTrialsXML:
        raise forms.ValidationError('Invalid file')

4. Chamada de um dos dois métodos de carregamento do XML: parse_opentrials ou parse_ictrp. Ambos recebem a instância de etree como argumento. Esse carregamento ainda não efetiva a persistência dos ensaios, pois este é um momento oportuno para verificar se tais ensaios existem e confirmar a importação deles com o usuário;

Exemplo:

if xml_format == 'opentrials':
    imp.parse_opentrials(tree)
else:
    imp.parse_ictrp(tree)

5. Importação de fato, usando o método import_parsed, que recebe o argumento if_exists, sendo que este pode receber uma de três opções:

UPDATE_IF_EXISTS - atualiza os ensaios existentes, e no caso dos campos não informados, mantém como estão;

REPLACE_IF_EXISTS - substitui os ensaios existentes, ou seja, os campos não informados no XML receberão seus valores default;

SKIP_IF_EXISTS - salto os ensaios existentes, ou seja, não faz nada com eles.

Exemplo:

imp.import_parsed(if_exists=REPLACE_IF_EXISTS)

O método import_parsed pode ser chamado independentemente dos demais, mas para isso, ele espera que exista um atributo _parsed_trials na classe, que é criado pelos métodos parse_opentrials e parse_ictrp. Portanto, no OpenTrials, nosso formulário de importação guarda o valor desse atributo na sessão, para que o usuário possa visualizar os ensaios que foram carregados e marcar aqueles que deseja importar de fato.

Há ainda outro meio mais prático para fazer o parsing (carregamento) e importação em um mesmo momento, que é usar os métodos import_opentrials(filename_or_xmltree, if_exists) ou import_ictrp(filename_or_xmltree, if_exists)

Todo o procedimento acima pode ser conferido nos módulos reviewapp.views e reviewapp.forms, onde estão respectivamente a view e os forms usados para o upload de arquivos XML com ensaios. Classes FossilClinicalTrial? e FossilContact?

Como já citamos, a implementação do django-fossil no OpenTrials usa o máximo do que ele pode oferecer, e um desses recursos é o de customizar o objeto de proxy que será retornado quando um fossil for carregado e lido. É para isso que existem as classes FossilClinicalTrial? e FossilContact?, do módulo repository.serializers. Ambas trabalham retornando valores para os atributos requisitados e tratando alguns deles, especialmente no aspecto das traduções.