APEX Data Loading - Add a column programatically - oracle-apex

I am trying to use the APEX data loader to allow users to import data and one of the column values I would rather not force the users to provide since the value is readily available in the session when the users selects import.Is there a way to add that column value programmatically?

With the Oracle APEX 4.2 data wizard, adding the rows to the SPREADSHEET_CONTENT is not enough if the First Rows are the column names. You also have to run the following assuming you are adding a 9th column, 'C009' and the name is 'MY_COLUMN_NAME'.
APEX_COLLECTION.ADD_MEMBER(
p_collection_name => 'PARSE_COL_HEAD',
p_c001 => 'C009',
p_c002 => 'MY_COLUMN_NAME')
;
Otherwise, it just ignores the data as it's using the meta data of PARSE_COL_HEAD.

I had a similar issue and solved it along the lines that Greg suggested in his comment. Here is a sample of the code I used in the process that runs right after "Parse Uploaded Data" on page 1 of the wizard:
FOR UPLOAD_ROW IN (SELECT SEQ_ID
FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '2',
p_attr_value => :P1_TABLE_FIELD_2);
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '3',
p_attr_value => :P1_TABLE_FIELD_3);
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '4',
p_attr_value => :P1_TABLE_FIELD_4);
END LOOP;
For a more detailed explanation see http://www.jrweth.com/oracle-apex-data-loader-part-1-adding-custom-columns/

You could edit the loader and add a data transformation rule.
I hate to just post a link but this blog post from Rowan provides ample explanation how this works since the documentation from Oracle does not.
Application Express Data Loader Transformation Rules in 4.2
Basically, the transformation rules feature allows you to select one or more columns in your target table. You then choose the type of transformation from the available options: PL/SQL expression, PL/SQL Function, SQL Query (2 types her).
The loader will run this function for each row loaded and you can reference session state, lookup values in other tables or package state, as well as column values in the row. For example if you have a column named COLUMN1 you would refer to it in the source of the transformation rule as :COLUMN1 (e.g IF :COLUMN1 > 10 THEN .... ).
If you use the PL/SQL Expression, the value after evaluation will be returned as the new column value. If you use PL/SQL Function you will need to add a RETURN expression to get this value back tot he column.
That is to say that you will not be able to set the values using the bind notation you use to get values. So if you want COLUMN1 to be set to 10, build a rule to set that value, don't try to do :COLUMN1 := 10; as this state will only last during the execution of your rule for the record being transformed.

This new code is working perfectly despite the USE_APPLICATION_DATE_FORMAT column added by APEX. Note that near the bottom of the code the p_seq is set to '35'.
Declare
v_date date;
BEGIN
SELECT SYSDATE INTO v_date FROM Dual;
APEX_COLLECTION.ADD_MEMBER(
p_collection_name => 'PARSE_COL_HEAD',
p_c001 => 'C036',
p_c002 => 'UPLOAD_DTE');
APEX_COLLECTION.MOVE_MEMBER_DOWN(
p_collection_name =>'PARSE_COL_HEAD',
p_seq => '36');
FOR UPLOAD_ROW IN (SELECT SEQ_ID FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '35',
p_attr_value => v_date);
END LOOP;
END;

For Oracle APEX data loader 4.2, in the developer view, look on the 1st page, in the 'Page Processing' block, right click on "Processing" and select "add new process" , then name it. Mine is called 'add_column', then add the below code.
You need to first add a new column to the APEX collection. You can do APEX_COLLECTION.ADD_MEMBER() function. In the code below, I add in an additional column at column 31 location and the name of the column is called 'FILE_DATE'. This is the same as the name as the column the database table.
After that you loop through each row by SEQ_ID and update column 31 with a value. In my case, I wanted to get the filename, which was :P25_FILE_NAME
When you run the pages, you can click on 'Session' on the developer toolbar at the bottom to have a look at the APEX collection array and see if your column is getting populated correctly.
BEGIN
APEX_COLLECTION.ADD_MEMBER
(
p_collection_name => 'PARSE_COL_HEAD',
p_c001 => 'C031',
p_c002 => 'FILE_DATE');
FOR UPLOAD_ROW IN (SELECT SEQ_ID FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => '31',
p_attr_value => :P25_FILE_NAME
);
END LOOP;
END;

I am able to get through it using the code below:
Declare
v_count number(10);
BEGIN
APEX_COLLECTION.ADD_MEMBER(
p_collection_name => 'PARSE_COL_HEAD',
p_c001 => 'SESSION_ID');
v_count:=APEX_COLLECTION.COLLECTION_MEMBER_COUNT ('PARSE_COL_HEAD');
v_count:=v_count+1;
FOR UPLOAD_ROW IN (SELECT SEQ_ID
FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE (
p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => v_count,
p_attr_value => V('APP_SESSION')); –-I was trying to load session id
END LOOP;
END;

Declare
v_count number(10);
BEGIN
v_count:=APEX_COLLECTION.COLLECTION_MEMBER_COUNT ('PARSE_COL_HEAD');
v_count:=v_count;
APEX_COLLECTION.ADD_MEMBER( p_collection_name => 'PARSE_COL_HEAD',
p_c001 => concat('C00',v_count),
p_c002 => 'ID_OFFICE_LOCATION');
APEX_COLLECTION.MOVE_MEMBER_DOWN( p_collection_name =>'PARSE_COL_HEAD',
p_seq => v_count+1);
FOR UPLOAD_ROW IN (SELECT SEQ_ID
FROM APEX_COLLECTIONS
WHERE COLLECTION_NAME = 'SPREADSHEET_CONTENT')
LOOP
APEX_COLLECTION.UPDATE_MEMBER_ATTRIBUTE ( p_collection_name => 'SPREADSHEET_CONTENT',
p_seq => UPLOAD_ROW.SEQ_ID,
p_attr_number => v_count,
p_attr_value => :P0_ID_OFFICE_LOCATION);
END LOOP;
END;

Related

Spinning circle doesn't disappear after process is finished

Apex 20.1
I have an interactive report which has one clickable column that I used to download a file from Apex to the user's computer.
Originally I had the link call an application process which worked beautifully. However I found that larger files would take time to download, and there was no "spinning circle" to indicate that the user should keep waiting, or that anything was happening.
So I put the download code in an "After Submit" process with a "Request=" server-side condition.
The link from my interactive report now calls the process with a URL thus:
javascript:var submit =apex.submit({request:"FETCH_ATTACHMENT", showWait:true, set:{"P4_MESSAGE_ID":#MESSAGE_ID#, "P4_PART_ID":#PART_ID#, "P4_PART_NAME":"#PART_NAME#"}});
The "showWait" works beautifully now, I get a spinning circle, except now I have the opposite problem. After the download is finished, the page is still greyed-out with the spinning circle. The only way to bring it back is to refresh the page.
I would like the spinning circle to go away once the file is downloaded and control to be returned back to the page.
Here is the code, it's still in dev so excuse the debug untidiness!
DECLARE
vclob CLOB;
vclobtmp CLOB;
vblob BLOB;
vmimetype VARCHAR2(1000);
v_position INTEGER;
v_progress VARCHAR2(100);
v_error varchar2(1000);
FUNCTION Clobfromblob (p_blob BLOB)
RETURN CLOB
IS
l_clob CLOB;
l_dest_offsset INTEGER := 1;
l_src_offsset INTEGER := 1;
l_lang_context INTEGER := dbms_lob.default_lang_ctx;
l_warning INTEGER;
BEGIN
IF p_blob IS NULL THEN
RETURN NULL;
END IF;
dbms_lob.Createtemporary(lob_loc => l_clob, CACHE => FALSE);
dbms_lob.Converttoclob(dest_lob => l_clob, src_blob => p_blob,
amount => dbms_lob.lobmaxsize, dest_offset => l_dest_offsset,
src_offset => l_src_offsset, blob_csid => dbms_lob.default_csid,
lang_context => l_lang_context, warning => l_warning);
RETURN l_clob;
END;
BEGIN
--raise_application_error(-20000,'P4_MESSAGE_ID '||:P4_MESSAGE_ID||' P4_PART_ID '||:P4_PART_ID||' :P4_PART_NAME '||:P4_PART_NAME);
delete from apps.barry_debug;
insert into apps.barry_debug(time,debug) values (sysdate, 'start1'); commit;
dbms_lob.Createtemporary(lob_loc => vclob, CACHE => TRUE,
dur => dbms_lob.call);
dbms_lob.Createtemporary(lob_loc => vclobtmp, CACHE => TRUE,
dur => dbms_lob.call);
dbms_lob.Createtemporary(lob_loc => vblob, CACHE => TRUE,
dur => dbms_lob.call);
SELECT part_data,
Substr(part_type, 1, Instr(part_type, ';') - 1)
INTO vblob, vmimetype
FROM source_data
WHERE message_id = :P4_MESSAGE_ID --APEX_APPLICATION.g_x01
AND part_id = :P4_PART_ID --APEX_APPLICATION.g_x02
;
v_progress := 'blob to clob';
insert into apps.barry_debug(time,debug) values (sysdate, 'clob1'); commit;
vclobtmp := Clobfromblob(vblob);
--Strip the header from the attachment data
v_position := dbms_lob.Instr(vclobtmp, 'Content-Transfer-Encoding: base64',
1, 1
)
+ 33;
dbms_lob.Copy(vclob, vclobtmp, dbms_lob.Getlength(vclobtmp), 1, v_position);
v_progress := 'decodebase64';
insert into apps.barry_debug(time,debug) values (sysdate, 'decodebase'); commit;
vblob := apex_web_service.Clobbase642blob(vclob);
--vblob := apps.P_Base64.DecodeToBlob (vClob);
sys.htp.init;
v_progress := 'mime_header';
sys.owa_util.Mime_header(vmimetype /*'application/octet-stream'*/, FALSE
/*,'UTF-8' */);
sys.htp.P('Content-length: '
|| sys.dbms_lob.Getlength(vblob));
sys.htp.P('Content-Disposition: attachment; filename="'
||:P4_PART_NAME
||'"');
sys.owa_util.http_header_close;
v_progress := 'download_file';
insert into apps.barry_debug(time,debug) values (sysdate, 'download'); commit;
sys.wpg_docload.Download_file(vblob);
insert into apps.barry_debug(time,debug) values (sysdate, 'release1'); commit;
dbms_lob.Freetemporary (vblob); --do not forget!!
insert into apps.barry_debug(time,debug) values (sysdate, 'release2'); commit;
dbms_lob.Freetemporary (vclob); --do not forget!!
insert into apps.barry_debug(time,debug) values (sysdate, 'release3'); commit;
dbms_lob.Freetemporary (vclobtmp); --do not forget!!
apex_application.stop_apex_engine; --this is needed to download file in background. But it raises an exception below.
insert into apps.barry_debug(time,debug) values (sysdate, 'finished'); commit;
EXCEPTION
when apex_application.e_stop_apex_engine then
raise; -- raise again the stop Application Express engine exception
WHEN no_data_found THEN
NULL;
when others then
--raise_application_error(-20000,'Error-fetch-attachment-'||v_progress||' '||sqlerrm);
v_error := sqlerrm;
insert into apps.barry_debug(time,debug) values (sysdate, 'Error-fetch-attachment-'||v_progress||'*'||v_error);
END;
The Debugging table shows that processing ends after the engine is stopped. (If I take this out nothing downloads at all).
P.S I have tried to ask the question on Oracle community help and got one answer which was to
paste this code into the Function and Global Variable Declarations. No difference.
$(document).ajaxComplete(function(){
var overlay$ = $("#apex_wait_overlay"),
processing$ = $(".u-Processing");
if(overlay$.length || processing$.length){
overlay$.remove();
processing$.remove();
}
});

collect data via API from the table "apex_activity_log" in APEX ORACLE

I have one question, I want to have one page view statistics and send them through REST.
here is my request:
select count (*)
from apex_activity_log l
where flow_id = 100
and time_stamp> = sysdate - (1/24/60/60 * 2419200)
and userid is not null
and step_id = 172
;
answer : 867
This query works great in "SQL Commands". But when I use this query in Packages I get 0. Although the answer should be completely different. How do I give Packages access to apex_activity_log. That the correct answer came back to me. Thanks)
My api in Packages :
PROCEDURE post_connect_service (
p_status OUT NUMBER,
p_blob IN BLOB
) IS
v_blob blob := p_blob;
v_clob CLOB;
tv apex_json.t_values;
v_id varchar2(1000);
v_number varchar2(1000);
v_date_last date;
v_count_v_pl int;
v_count_sum_v int;
BEGIN
v_clob := iot_general.blob_to_clob(v_blob);
apex_json.parse(tv,v_clob);
v_id := apex_json.get_varchar2(p_path => 'id', p_values => tv);
select count(*) into v_count_sum_v
from apex_activity_log;
p_status := 200;
apex_json.open_object;
apex_json.write('success', true);
apex_json.write('count_views', v_count_v_pl);
apex_json.write('count_sum_views', v_count_sum_v);
apex_json.close_object;
EXCEPTION
WHEN OTHERS THEN
p_status := 500;
apex_json.open_object;
apex_json.write('success', false);
apex_json.write('message', substr(sqlerrm,1,4000));
apex_json.close_object;
END post_connect_service;
The view apex_activity_log has data for the current apex context. If it is queried outside of an apex context, it will return no rows. Easiest way is to create an apex session before querying it.
koen>SELECT COUNT(*) FROM apex_activity_log;
COUNT(*)
___________
0
koen>DECLARE
2 BEGIN
3 apex_session.create_session (
4 p_app_id => 286,
5 p_page_id => 1,
6 p_username => 'JOHN.DOE');
7 END;
8* /
PL/SQL procedure successfully completed.
koen>SELECT COUNT(*) FROM apex_activity_log;
COUNT(*)
___________
9327
koen>
first, there is a pre-configured REST API for this within ORDS.
https://docs.oracle.com/en/database/oracle/oracle-database/21/dbrst/api-oracle-apex.html
To answer your actual question:
Does your procedure run within the same workspace as application 100 is? Or are you dependent on the APEX_ADMINISTRATOR_ROLE for this to return rows?
if the latter, make sure to create your package or procedure with AUTHID CURRENT_USER to make sure that it ...
a) runs with the privieges of the invoking user
b) have roles (such as the APEX_ADMINISTRATOR_ROLE) enabled during PL/SQL execution.

Load data from table into apex_util.select_list multiple

I have a Classic Report with a schedule for each day. There are no, one or multiple entries per day. It looks like this:
select
week_no,
apex_item.DISPLAY_AND_SAVE(p_idx => 8, p_value => plan_id) as ID,
apex_item.select_list(p_idx => 1, p_list_values => 'S1;1,S2;2,S3;3', p_attributes => 'size="3" multiple="multiple"', p_show_null => 'NO', p_show_extra => 'NO', p_item_id => plan_id || '_1', p_value => monday) as monday,
apex_item.select_list(p_idx => 2, p_list_values => 'S1;1,S2;2,S3;3', p_attributes => 'size="3" multiple="multiple"', p_show_null => 'NO', p_show_extra => 'NO', p_item_id => plan_id || '_2', p_value => tuesday) as tuesday,
apex_item.select_list(p_idx => 3, p_list_values => 'S1;1,S2;2,S3;3', p_attributes => 'size="3" multiple="multiple"', p_show_null => 'NO', p_show_extra => 'NO', p_item_id => plan_id || '_3', p_value => wednesday) as wednesday
from plan;
I can select multiple values and can save them to my table. It gets save like this "1,2". But I can't load the data rows with multiple selected values.
I can select multiple options with jQuery like this
$('#1000_1').val(["1","2"]);
but I don't know how to select the stored values from table with PL/SQL and then set the options selected with javascript.
I tried to load the data in an apex_item.text (id="1000_11" for monday etc.) and transfer the values in "After Refresh" DA to select list like that:
var sel_opt = $('#1000_11').val();
if (sel_opt == "1,2")
{
$('#1000_1').val(["1","2"]);
}
else if (sel_opt == "1,3")
{
$('#1000_1').val(["1","3"]);
}
else if (sel_opt == "2,3")
{
$('#1000_1').val(["2","3"]);
}
else if (sel_opt == "1,2,3")
{
$('#1000_1').val(["1","2","3"]);
}
It works, but maybe there's a smarter solution?
I create a sample Workspace on https://apex.oracle.com/pls/apex/f?p=4550:1:0:::::
Workspace: multi_select
Username: dev
Password: newYears3problems
I would create SELECT LIST page items for each selection menu and set their multi selection option to Yes.
then I'm guessing I'd need something like this somewhere at my page-process section
declare
array apex_application_global.vc_arr2;
begin
array := apex_util.string_to_table (:P30_SELECT_LIST_1);
apex_collection.create_or_truncate_collection ('P30_SELECT_LIST_1');
apex_collection.add_members ('P30_SELECT_LIST_1', array);
end;
to store more than one menu item at once.
then I'd add something like that to my query
and ITEM_1 in (
select c001
from apex_collections
where collection_name = 'P30_SELECT_LIST_1'
)
I probably didn't understand your question, but according to part that I get, it's straightforward sql query. no need for js.

how to get the column values from interactive grid to items

I have a interactive grid with column email_to email_from which is editable grid, now i need to fetch the values which we are entering into columns in grid..my items are :p1_email_to,:p1_email_from..
below is my query which is calling the email procedure, here i need to pass the item values which i am entering on screen but the item values are showing null, the grid values are not returning to items.
Here the query for on submit button:
DECLARE
p_division EMAIL_ERRORS.ORG_ID%TYPE;
p_to EMAIL_ERRORS.TO_NAME%TYPE;
p_from EMAIL_ERRORS.FROM_NAME%TYPE;
p_message EMAIL_ERRORS.MESSAGE_TXT%TYPE;
p_user EMAIL_ERRORS.USER_NAME%TYPE;
p_sys_param_from EMAIL_ERRORS.SYS_PARAM_FROM_TXT%TYPE;
p_attach EMAIL_ERRORS.ATTACH_TXT%TYPE;
P_POST_OP EMAIL_ERRORS.POST_OP_TXT%TYPE;
P_POST_OP_PARAM1 EMAIL_ERRORS.POST_OP_PARAM1_TXT%TYPE;
P_POST_OP_PARAM2 EMAIL_ERRORS.POST_OP_PARAM2_TXT%TYPE;
P_POST_OP_PARAM3 EMAIL_ERRORS.POST_OP_PARAM3_TXT%TYPE;
BEGIN
CAB$MAIL.send (p_to => :P1_TO_NAME
,p_from => :P1_FROM_NAME
,p_message => :P1_MESSAGE_TXT
,p_user => :P1_USER_NAME
,p_sys_param_from => :P1_SYS_PARAM_FROM_TXT
,p_division => :P1_ORG_ID
,p_attach => :P1_ATTACH_TXT
,P_POST_OP => :P1_POST_OP_TXT
,P_POST_OP_PARAM1 => :P1_POST_OP_PARAM1_TXT
,P_POST_OP_PARAM2 => :P1_POST_OP_PARAM2_TXT
,P_POST_OP_PARAM3 => :P1_POST_OP_PARAM3_TXT);
END;

Axlsx Formatting

Is it possible to format both a row and column?
For instance I am doing a loop that uses the index to style rows different colors based on if it is even or odd but I also want to style a column that has percentages to use the :num_fmt => 9
Also why when I am presenting the number as something like 1.2 does that end up changing it to 120%, all I want is for that data to look like 1.2%
#people.each_with_index | person, index |
if index.odd?
sheet.add_row [person['name'], person['rate']]
else
sheet.add_row [person['name'],person['rate']], :style => even_row
end
end
(my even row style is set at the top of my code)
I figured this out, you need to do set your style using something like
percent = s.add_style(:num_fmt => 9)
even_row_percent = s.add_style(:bg_color => 'blue', :fg_color => 'white', :b => false, :format_code => 0%)
even_row = s.add_style(:bg_color => 'blue', :fg_color =>'white', :b => false)
then in your loop just use an each with index and then use a if statement like
if index.odd
sheet.add_row[
item[:value],
item[:value_percent]], :style => [nil, percent]
else
sheet.add_row[
item[:value],
item[:value]], :style => [even_row, even_row_percent]
end