logo

30 апр. 2010 г.

BIEE: множество страниц дашборда

Если у вас большое количество BI Answer’-ов, которые желательно логически группировать, то, наверное, вам знакома ситуация:



Множество страниц информационной панели бывает необходимостью. Но это несет с собой массу неудобств: становится тяжело ориентироваться во вкладках информационной панели; если вам нужно отредактировать конкретную страницу, то, чтобы добраться до кнопки редактирования, приходится скролить по горизонтали.

Возможный вариант решения – это создание заголовочной страницы, на которой размещаются ссылки на все остальные страницы информационной панели.
Причем ссылки должны быть навигационными – для осуществления передачи контекста (переменные презентации).


(Горизонтальную черту под ссылкой можно убрать правкой css)

По-моему, минусы такого подхода очевидны – вам придется постоянно самим следить за корректностью ваших ссылок. При создании новой страницы информационной панели не забыть создать и ссылку на нее.

Хотелось бы автоматизма. Например, как в компоненте «Папка».


Но «Папка» позволяет показывать лишь элементы-Answer’ы. Страницы панели она не выводит.
Это плохо, но не смертельно.

OBIEE предоставляет нам массу веб-сервисов, среди которых есть и сервис получения списка дочерних элементов.

Создадим PL/SQL-функцию, которая будет возвращать нам перечень страниц указанной информационной панели.
Этот перечень будем отображать через Direct Database Request. Причем форматированием результатов Request’а добьемся возможности навигации на эти страницы.

Создадим типы, они потребуется в дальнейшем:

create or replace type bi_db_page as object
(
page VARCHAR2(150),
portalpath VARCHAR2(255)
);

create or replace type bi_db_page_array as table of bi_db_page;


Создадим пакет, содержащий всю необходимую логику:

CREATE OR REPLACE PACKAGE BI_UTILS IS

FUNCTION GET_DASHBOARD_PAGES(P_DASHBOARD_PATH IN VARCHAR2)
RETURN bi_db_page_array
PIPELINED;

END BI_UTILS;


CREATE OR REPLACE PACKAGE BODY BI_UTILS IS

g_nqs_user varchar2(50) := 'Administrator';
g_nqs_password varchar2(50) := 'Administrator';

g_bi_wsdl_url varchar2(255) := 'http://localhost:9704/analytics/saw.dll?SoapImpl=';

g_wsdl_session_service varchar2(50) := 'nQSessionService';
g_wsdl_webcat_service varchar2(50) := 'webCatalogService';

G_RET_STS_SUCCESS constant varchar2(1) := 'S';
G_RET_STS_ERROR constant varchar2(1) := 'E';
G_RET_STS_UNEXP_ERROR constant varchar2(1) := 'U';

procedure http_post(p_url_in in varchar2,
p_data_in in varchar2,
p_data_type in varchar2 default 'text/xml',
p_action in varchar2 default null,
p_charset in varchar2 default null,
p_username_in in varchar2 default null,
p_password_in in varchar2 default null,
x_status_code out varchar2,
x_reason_phrase out varchar2,
x_response out varchar2) is
l_data_in varchar2(32767);
l_http_req utl_http.req;
l_http_resp utl_http.resp;
l_response varchar2(32767);
begin

utl_http.set_response_error_check(false);

l_http_req := utl_http.begin_request(p_url_in, 'POST');

utl_http.set_header(l_http_req, 'content-type', p_data_type);

l_data_in := convert(p_data_in, 'utf8');

utl_http.set_header(l_http_req, 'content-length', length(l_data_in));
utl_http.set_header(l_http_req, 'SOAPAction', p_action);

if p_username_in is not null then
utl_http.set_authentication(l_http_req, p_username_in, p_password_in);
end if;

utl_http.set_body_charset(l_http_req, null);
utl_http.write_text(l_http_req, l_data_in);

l_http_resp := utl_http.get_response(l_http_req);

if (l_http_resp.status_code = utl_http.HTTP_OK) then
x_status_code := G_RET_STS_SUCCESS;
else
x_status_code := G_RET_STS_ERROR;
end if;
x_reason_phrase := l_http_resp.reason_phrase;

utl_http.read_text(l_http_resp, l_response);
utl_http.end_response(l_http_resp);
x_response := l_response;

EXCEPTION
when others then
utl_http.end_response(l_http_resp);
x_status_code := G_RET_STS_UNEXP_ERROR;
x_reason_phrase := sqlerrm;
END http_post;

function get_error_response_text(p_response in varchar2) return varchar2 is
l_parser xmlparser.Parser;
l_domDoc xmldom.DOMDocument;
l_domNL xmldom.DOMNodeList;
l_domN xmldom.DOMNode;
l_ret varchar2(32767);
begin
l_parser := xmlparser.newParser;
xmlparser.parseclob(l_parser, p_response);
l_domDoc := xmlparser.getDocument(l_parser);
Xmlparser.Freeparser(l_parser);
l_domNL := xmldom.getElementsByTagName(l_domDoc, 'faultstring');
l_domN := xmldom.getFirstChild(xmldom.item(l_domNL, 0));

l_ret := xmldom.getNodeValue(l_domN);

xmldom.freeDocument(l_domDoc);

return l_ret;
end get_error_response_text;

FUNCTION GET_DASHBOARD_PAGES(P_DASHBOARD_PATH IN VARCHAR2)
RETURN bi_db_page_array
PIPELINED IS

l_session_id varchar2(150);

l_status_code varchar2(1);
l_reason_phrase varchar2(2000);

l_send_data varchar2(32767);
l_receive_data varchar2(32767);

l_error_explanation varchar2(2000);

l_receive_xml xmltype;

BEGIN

/*login*/
l_send_data :=
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v5="com.siebel.analytics.web/soap/v5">' ||
' <soapenv:Header/>' ||
' <soapenv:Body>' ||
' <v5:logon>' ||
' <v5:name>' || g_nqs_user || '</v5:name>' ||
' <v5:password>' || g_nqs_password || '</v5:password>' ||
' </v5:logon>' ||
' </soapenv:Body>' ||
'</soapenv:Envelope>';

http_post(p_url_in => g_bi_wsdl_url || g_wsdl_session_service,
p_data_in => l_send_data,
p_action => 'logonRequest',
x_status_code => l_status_code,
x_reason_phrase => l_reason_phrase,
x_response => l_receive_data);

if (l_status_code = G_RET_STS_SUCCESS) then

l_receive_xml := XMLTYPE(l_receive_data);

SELECT EXTRACT(VALUE(xmlt),
'sessionID/text()',
'xmlns="com.siebel.analytics.web/soap/v5"') .getStringVal() AS session_id
into l_session_id
FROM TABLE(XMLSEQUENCE(EXTRACT(l_receive_xml,
'//sawsoap:sessionID[1]',
'xmlns:sawsoap="com.siebel.analytics.web/soap/v5"'))) xmlt;
else

begin
l_error_explanation := get_error_response_text(p_response => l_receive_data);
exception
when others then
l_error_explanation := l_reason_phrase ||
' (cannot parse BI response)';
return;
end;
--may be put it to log-table?

end if;

/*get pages info*/
l_send_data :=
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v5="com.siebel.analytics.web/soap/v5">' ||
' <soapenv:Header/>' ||
' <soapenv:Body>' ||
' <v5:getSubItems>' ||
' <v5:path>' || P_DASHBOARD_PATH || '</v5:path>' ||
' <v5:mask>*</v5:mask>' ||
' <v5:resolveLinks>true</v5:resolveLinks>' ||
' <v5:sessionID>' || l_session_id || '</v5:sessionID>' ||
' </v5:getSubItems>' ||
' </soapenv:Body>' ||
'</soapenv:Envelope>';

http_post(p_url_in => g_bi_wsdl_url || g_wsdl_webcat_service,
p_data_in => l_send_data,
p_action => 'getSubItemsRequest',
x_status_code => l_status_code,
x_reason_phrase => l_reason_phrase,
x_response => l_receive_data);

if (l_status_code = G_RET_STS_SUCCESS) then

l_receive_xml := XMLTYPE(l_receive_data);

for cur in (SELECT EXTRACT(VALUE(xmlt),
'itemInfo/path/text()',
'xmlns="com.siebel.analytics.web/soap/v5"') .getStringVal() AS path,
EXTRACT(VALUE(xmlt),
'itemInfo/caption/text()',
'xmlns="com.siebel.analytics.web/soap/v5"') .getStringVal() AS caption,
EXTRACT(VALUE(xmlt),
'itemInfo/signature/text()',
'xmlns="com.siebel.analytics.web/soap/v5"') .getStringVal() AS signature

FROM TABLE(XMLSEQUENCE(EXTRACT(l_receive_xml,
'//sawsoap:itemInfo',
'xmlns:sawsoap="com.siebel.analytics.web/soap/v5"'))) xmlt) loop

if (cur.signature = 'dashboardpageitem1') then
PIPE ROW(bi_db_page(rtrim(rtrim(cur.path, cur.caption),'/'),
cur.caption));
end if;

end loop;

else

begin
l_error_explanation := get_error_response_text(p_response => l_receive_data);
exception
when others then
l_error_explanation := l_reason_phrase ||
' (cannot parse BI response)';
return;
end;
--may be put it to log-table?

end if;

/*logoff*/
l_send_data :=
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v5="com.siebel.analytics.web/soap/v5">'||
' <soapenv:Header/>'||
' <soapenv:Body>'||
' <v5:logoff>'||
' <v5:sessionID>' || l_session_id || '</v5:sessionID>'||
' </v5:logoff>'||
' </soapenv:Body>'||
'</soapenv:Envelope>';


http_post(p_url_in => g_bi_wsdl_url || g_wsdl_session_service,
p_data_in => l_send_data,
p_action => 'logoffRequest',
x_status_code => l_status_code,
x_reason_phrase => l_reason_phrase,
x_response => l_receive_data);



return;
END GET_DASHBOARD_PAGES;

END BI_UTILS;



Не забудьте поменять в теле пакета значения глобальных переменных, отвечающих за логин-пароль BIEE, а также за URL доступа.

Теперь создадим Direct Database Request, использующий созданный пакет:


Убираем на этапе тестирования кеширование. В дальнейшем, если вы не собираетесь каждые 5 минут менять вашу информационную панель – добавлять/удалять/переименовывать ее страницы – то кеширование лучше включить.

Посмотрим, что получилось:



Так как нам нужно обеспечить функциональность навигации, то создадим “Narrative View” нашего ансвера.


Включим в раздел префикса определение javascript-функции:

<script language="javascript">
function getDBPagePath(sPortalPath, sPage)
{
var sCmd = sawCommandToURLImpl('Dashboard');
sCmd += "&PortalPath=" + saw.encodeURIComponent(sPortalPath);
sCmd += "&Page=" + saw.encodeURIComponent(sPage);
return sCmd
}
</script>

Данная функция генерирует корректную ссылку на страницу панели.

Саму строку с данными генерируем так:

<a href="javascript:void(null);" onclick="return GuidedNav(getDBPagePath('@1','@2'),true);">@2</a>


Не забываем включить галку «Содержит разметку HTML»

Поместим представление «Narrative View» в составную разметку и укажем в свойствах, что данные должны выравниваться по левому краю.



Поместим на нашу заголовочную страницу созданный ансвер.



Дадим осмысленное название разделу, а также скроем все страницы панели кроме заголовочной.




Вуаля!

3 комментария:

  1. select page, portalpath from table(bi_utils.get_dashboard_pages ('/shared/RVS/_Portal'))
    тут я понимаю надо укзать путь в каталоге презентаций от корня. По всякому пробовал - не работает

    ОтветитьУдалить
  2. красота
    а можно настроить агент так, чтобы одна из страниц панели была текстом письма, а остальные страницы вложенными файлами excel?

    ОтветитьУдалить
  3. Плохо понимаю задачу ;)
    Вы не могли бы связаться со мной через почту/hangout?

    ОтветитьУдалить