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.