Semana passada eu mostrei como montar um relatatório a partir de um metamodelo. Hoje eu vou encerrar o assunto com mais algumas dicas.
Concepts
Um dos pilares de um bom relatório é a qualidade da apresentação, e nesta qualidade inclui-se a consistência, ou seja, dados do mesmo tipo possuem a mesma aparência, ou no mínimo possuem algum padrão.
Um relatório ordinário, construído a partir de um SQL, tem seus campos formatados livremente. Se por um lado isso é bom, já que dá espaço irrestrito para o autor do relatório passar sua mensagem, por outra é ruim, porque obriga esse mesmo autor a uma formatação tediosa, repetitiva. Fora o aborrecimento de repetir sempre a mesma coisa, ainda podemos acabar esquecendo ou mudando algum detalhe ao longo do tempo.
Um concept, ou conceito, é um padrão de formatação definido dentro do metamodelo e obedecido integralmente pelo PRD. Acesse esta página para obter mais informações sobre concepts.
Vamos fazer um exercício: criaremos um conceito de texto destacado e aplicaremos ao relatório do post anterior. Primeiro, abrimos o PME e carregamos o metamodelo do Beltrano OLTP. Em seguida acessamos o editor de conceitos (concept editor):
Nele clicamos sobre o conceito Base e depois clicamos no sinal de + no canto superior direito dessa seção. Uma janela se abrirá, pedindo o nome do conceito – eu eu chamei de Destaque. Como criamos o novo conceito a partir do Base, Destaque herdou as propriedades deste. Caso contrário, se tívéssemos criado um conceito na raiz, ele teria nascido vazio e precisaria receber todos os atributos prentendidos.
Com o conceito Destaque selecionado, clique no elo ao lado do atributo Color of Text. Isso quebra a herança e nos permite redefinir aquele atributo, mantendo o restante:
Dê ok e, de volta à interface principal do PME, localize um campo na camada de apresentação – Neste exemplo eu selecionei Autor. Clique com o botão da direita sobre ele e selecione Set Parent Concept… para escolher o conceito desejado. Selecione Destaque na janela que se abrirá e Ok para aplicar.
Feito! Quando o campo Autor for usado no relatório, ele aparecerá em cor vermelha, em destaque:
Notou a festa do caqui que está o layout do relatório, na parte de cima da figura? Mas como o PRD vai respeitar o definido no metamodelo, o relatório vai sair arrumadinho, apenas com o nome do autor em vermelho!
Outro exemplo: digamos que precisamos formatar a coluna da direita como um valor monetário, usando a máscara R$ #.###,00
. Alteramos ou criamos um conceito no metamodelo, exportamos e reaplicamos o metamodelo à consulta previamente criada:
Reaplicar o metamodelo a um relatório aberto no PRD é meio chato. Como o PRD não embute o metamodelo no relatório, bastaria purgar o cache ou fechar o relatório e recarregá-lo para forçar o PRD a reler o XMI. Por puro e simples hábito, eu apelo para força bruta: simplesmente apago e recrio a fonte e as consultas (apelando para um copy-paste básico, já que ninguém é de ferro…)
Para relatórios publicados no BA Server não é preciso nada: basta republicar o modelo a partir do PME, ou recarregá-lo no quadro Manage Data Sources e já está valendo. A série 5 do BA Server veio com purga e recarga automática de metamodelos na republicação.
Só essa simplicidade e praticidade, na minha opinião, já é o bastante para fazer valer a pena usar um MQL ao invés de SQL.
Prompts
Um recurso crucial para relatórios são os “prompts”, ou filtros, que permitem ao usuário escolher uma determinada fatia dos dados para apresentação. O PRD oferece esse recurso sob o nome de parameters. Um parameter – ou prompt – é uma variável preenchida pelo usuário em tempo de execução, que entra na consulta como algum tipo de restrição. Por exemplo, a consulta abaixo retorna todos os clientes de um determinado “tipo”, filtrando a consulta pelo conteúdo do prompt tipo_selecionado
:
SELECT cliente, estado, cidade
WHERE tipo = ${tipo_selecionado}
Não é um mecanismo complicado, mas não é trivial o bastante para eu mostrá-lo aqui completamente. Assistam este vídeo que verão como fazer um prompt.
Quando usamos uma consulta MQL, de metadados, aplicar a restrinção é um pouco mais simples. Os passos são esses:
- Construa um relatório com MQL, como o que fizemos no post passado;
- Crie uma consulta (MQL, SQL, tanto faz) que retorne a lista de opções que o cliente pode selecionar;
- Crie o parâmetro no PRD usando essa consulta como fonte;
- Volte para a consulta principal e, usando a interface de criação de consultas, adicione o filtro e insira o parâmetro.
No nosso caso vamos filtrar o relatório de vendas do Alexandre por UF.
Primeiro, construímos uma consulta em MQL que traz a lista de estados:
O MQL dessa consulta é:
<mql>
<domain_type>relational</domain_type>
<domain_id>BeltranoOLTP</domain_id>
<model_id>BV_MODEL_1</model_id>
<model_name>Beltrano OLTP</model_name>
<options>
<disable_distinct>false</disable_distinct>
<limit>-1</limit>
</options>
<selections>
<selection>
<view>BC_CLIENTES_PF</view>
<column>BC_ESTADOS_UF_2</column>
<aggregation>none</aggregation>
</selection>
</selections>
<constraints/>
<orders>
<order>
<direction>asc</direction>
<view_id>BC_CLIENTES_PF</view_id>
<column_id>BC_ESTADOS_UF_2</column_id>
</order>
</orders>
</mql>
</pre>
Construímos um paramater alimentado por essa consulta, chamado UF:
Depois inserimos esse parâmetro, UF, na consulta que popula o relatório: colocamos a coluna que vai ser filtrada na seção constraints e aplicamos uma igualdade para o parâmetro.
O truque é envolver o parâmetro em chaves, { e }. Com isso o construtor de consultas reconhece que se trata de um parâmetro e não de um valor ordinário, e ajusta a consulta automaticamente. A título de comparação, observe o filtro para pegar apenas as vendas do Alexandre.
Ao adicionar um nome cercado por { e } nos filtros, o construtor de consulta realiza duas coisas:
- Define um parâmetro no início da consulta
<mql> <domain_type>relational</domain_type> <domain_id>BeltranoOLTP</domain_id> <model_id>BV_MODEL_1</model_id> <model_name>Beltrano OLTP</model_name> <options> <disable_distinct>false</disable_distinct> <limit>-1</limit> </options> <parameters> <parameter defaultValue="XX" name="UF" type="STRING"/> </parameters> <selections> <selection> <view>BC_PEDIDOS</view> <...restante da consulta...>
- E usa esse parâmetro na seção conditions:
<...começo da consulta...> <constraints> <constraint> <operator>AND</operator> <condition>CONTAINS([BC_PEDIDOS.BC_VENDEDOR_NOME_COMPLETO];"Alexandre")</condition> </constraint> <constraint> <operator>OR</operator> <condition>[BC_CLIENTES_PJ.BC_ESTADOS_UF_3] = [param:UF]</condition> </constraint> </constraints>
Ao rodar o relatório o PRD cria e apresenta um controle do tipo drop-down, populado com a lista dos estados. Sempre que um estado é selecionado, o relatório é automaticamente filtrado:
Um bom uso dessas possibilidades é criar relatórios que se adequam a cada usuário: basta usar o parâmetro pré-definido env::username
para filtrar a consulta pelo nome do usuário na plataforma desde que esse usuário possua um nome igual registrado no banco de dados.
Por exemplo, se eu registrar no Pentaho BA Server os vendedores com o mesmo nome que possuem no Beltrano OLTP, eu posso filtrar os dados montando uma constraint do tipo Vendedor CONTAINS {env::username}
.
Experimente!
8.3 Segurança
Por fim, mas não menos importante, um metamodelo pode restringir o conteúdo de uma consulta em função das permissões de acesso – controle de segurança – dos dados.
Como sempre, a idéia é simples: definimos um filtro de dados no metamodelo que usa o nome do usuário e, eventualmente, seu papel, para montar um controle de exibição baseado no controle de acesso.
Como fazer:
- No Pentaho BA Server, usando o papel de administrador, registre todos seus usuários. Eu fiz isso com os usuários da Beltrano;
- Com Pentaho Metadata Editor edite o metamodelo e registre um filtro no nível do modelo de negócio, seção Data Constraints: qualquer consulta contra esse modelo de negócio vai embutir aquele filtro automaticamente. Salve e publique o metamodelo no servidor;
- No PRD basta remover qualquer filtro local. Por exemplo, eu removi o filtro de usuário “Alexandre”, deixando o relatório trazer dados de todos os vendedores, indistintamente. Salvei e publiquei o metamodelo.
Feito! :-)
Para testar, acessei a plataforma com duas contas: Fábio e Alexandre. Como o relatório consome metadados, e os metadados são completamente filtrados por meio do atributo Data Constraints no Business Model, os dados apresentados já estão filtrados.
8.4 Conclusão
“Nossa, Fábio! Que maravilha!”, dirão vocês, “e não tem nenhuma desvantagem?”
Sim, tem: justamente adicionar uma outra camada para manutenção. Se usamos apenas SQL, qualquer alteração no banco entra em efeito imediatamente para o relatório. Se usarmos um metamodelo vamos precisar atualizar a camada de metadados antes de poder sequer testar as mudanças no PRD. Fazer uma pequena alteração na formatação pode forçar o analista a quebrar o vínculo do campo com a metaconsulta, perdendo a vantagem do modelo centralizado, ou então obrigar a uma atualização do metamodelo, cascateando a publicação de uma nova versão.
Há uma segunda desvantagem, mas é mais sutil e, talvez, pior: aparentemente não há como fazer UNIONs! Isso não é um problema para modelos bem comportados, mas modelos como o do Beltrano – que nem é tão exótico assim – se quebram quando tentamos fazer certas combinações numa só consulta. Por exemplo, no modelo transacional, não é possível escrever um único SQL que traga todos os clientes PF e PJ. É preciso duas consultas, coladas a posteriori com UNION, para então chegar ao conjunto completo. E eu não achei como fazer isso com MQL.
Ou seja, as vantagens de uso de um metamodelo vêm com um preço: um processo de desenvolvimento com mais alguns passos. Se isso vai compensar no final é uma questão respondida projeto a projeto.
Até a próxima! :-)