Program source to generate SAP code statistic for Gource visualization
Below is source code of program to generate SAP code statistic for Gource visualization This blog post how to use geerated statistic file for generating video vizualization of it can be found here – SAP code visualization – make a cool video of your project evolution.
Just create new Z-program in SAP called ZAR_GENERATE_CODE_STATISTIC using SE38 or SE80 transaction and copy/paste line belows into it (line numbers would be not copied).
*&---------------------------------------------------------------------*
*& Report ZAR_GENERATE_CODE_STATISTIC
*& Generate SAP code statistic for Gource visualization
*&---------------------------------------------------------------------*
*& Author: Artem Ruzak
*& Since: 24 Jan 2012
*&---------------------------------------------------------------------*
REPORT zar_generate_code_statistic.
*&---------------------------------------------------------------------*
*& TYPE-DEFINITIONS
*&---------------------------------------------------------------------*
TYPE-POOLS: trwbo, strhi, trlck, trsel, ctslg, slis, sctsc.
CLASS cl_ixml DEFINITION LOAD.
TABLES sscrfields.
TYPES: BEGIN OF xml_str,
data(132) TYPE c,
END OF xml_str.
TYPES: BEGIN OF xml_line,
data(256) TYPE x,
END OF xml_line.
DATA:
lo_conv TYPE REF TO cl_abap_conv_out_ce,
lv_objtype TYPE trobjtype,
lt_string_components TYPE TABLE OF swastrtab,
ls_string_components TYPE swastrtab,
lt_xml_str TYPE TABLE OF xml_str,
lo_ixml TYPE REF TO if_ixml,
lo_streamfactory TYPE REF TO if_ixml_stream_factory,
lo_istream TYPE REF TO if_ixml_istream,
lo_parser TYPE REF TO if_ixml_parser,
parseerror TYPE REF TO if_ixml_parse_error,
lo_ostream TYPE REF TO if_ixml_ostream,
lo_renderer TYPE REF TO if_ixml_renderer,
lo_xml_doc TYPE REF TO if_ixml_document,
lo_root TYPE REF TO if_ixml_element,
lo_node_pointer TYPE REF TO if_ixml_node,
lo_comment TYPE REF TO if_ixml_comment,
lo_element TYPE REF TO if_ixml_element,
lo_new_element TYPE REF TO if_ixml_element,
lo_path_element TYPE REF TO if_ixml_element,
lo_attribute TYPE REF TO if_ixml_attribute,
lo_filter1 TYPE REF TO if_ixml_node_filter,
lo_filter2 TYPE REF TO if_ixml_node_filter,
lo_filter3 TYPE REF TO if_ixml_node_filter,
lo_filter4 TYPE REF TO if_ixml_node_filter,
lo_iterator TYPE REF TO if_ixml_node_iterator,
lv_value TYPE string,
lv_value2 TYPE string,
lv_func_desc LIKE ko013-trfunctn_l,
lt_name TYPE TABLE OF char100,
lv_name TYPE string,
lv_obj_name TYPE string,
lv_subobj_name TYPE string,
lo_text TYPE REF TO if_ixml_text,
lv_xml_string TYPE string,
lv_xml_xstring TYPE xstring,
lt_xml_table TYPE TABLE OF xml_line,
lv_xml_size TYPE i,
ls_request_header TYPE trwbo_request_header,
lt_request_headers TYPE trwbo_request_headers,
ls_request TYPE trwbo_request,
gv_sysk_pattern LIKE e070-trkorr,
system_name TYPE sysysid,
system_type TYPE sysysid,
gt_object_texts LIKE ko100 OCCURS 10,
ls_object_texts LIKE ko100,
lt_objects TYPE TABLE OF e071,
ls_objects TYPE e071,
ls_selection TYPE trwbo_selection,
ls_popup TYPE strhi_popup,
ls_ranges TYPE trsel_ts_ranges,
lv_filename TYPE string,
lv_req_num TYPE i,
lv_perc_tmp TYPE f,
lv_perc TYPE i VALUE 0,
str TYPE string,
line TYPE i,
column TYPE i,
count TYPE i,
index TYPE i,
lv_pointer_tstm TYPE timestamp,
lv_tstm TYPE timestamp,
lv_uname TYPE xubname,
ls_uaddr TYPE bapiaddr3,
lt_bapiret TYPE bapirettab,
lt_obj_type TYPE TABLE OF string,
lv_obj_type TYPE string,
lv_short_name_len TYPE i,
lv_short_name TYPE c LENGTH 8.
SELECTION-SCREEN BEGIN OF BLOCK mysel WITH FRAME TITLE tit.
SELECTION-SCREEN BEGIN OF LINE.
SELECTION-SCREEN COMMENT 1(15) pref.
PARAMETERS: p1(30) TYPE c LOWER CASE.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN BEGIN OF LINE.
SELECTION-SCREEN COMMENT 1(15) file.
PARAMETERS: f1 LIKE ibipparms-path VISIBLE LENGTH 30 .
SELECTION-SCREEN PUSHBUTTON 50(13) but2 USER-COMMAND cli2.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERS: c1 TYPE xfeld AS CHECKBOX.
SELECTION-SCREEN COMMENT 3(45) append.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERS: c2_note TYPE xfeld AS CHECKBOX.
SELECTION-SCREEN COMMENT 3(45) no_note.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERS: c3_shnam TYPE xfeld AS CHECKBOX DEFAULT 'X'.
SELECTION-SCREEN COMMENT 3(45) shrtnam.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN END OF BLOCK mysel.
SELECTION-SCREEN BEGIN OF BLOCK obj_sel WITH FRAME TITLE sel_t.
SELECTION-SCREEN BEGIN OF LINE.
SELECTION-SCREEN COMMENT 1(20) obj_seli.
SELECT-OPTIONS s_obj_i FOR lv_objtype.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN BEGIN OF LINE.
SELECTION-SCREEN COMMENT 1(20) obj_sele.
SELECT-OPTIONS s_obj_e FOR lv_objtype.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN END OF BLOCK obj_sel.
AT SELECTION-SCREEN.
CASE sscrfields-ucomm.
WHEN 'CLI2'. "select file from disk
CALL FUNCTION 'F4_FILENAME'
EXPORTING
program_name = syst-cprog
dynpro_number = syst-dynnr
field_name = ' '
IMPORTING
file_name = f1.
* WHEN 'ONLI'.
* .
* WHEN OTHERS.
* LEAVE.
ENDCASE.
INITIALIZATION.
tit = 'General generation settings'.
pref = 'Path prefix'.
file = 'Save to file'.
append = 'Merge with existing'.
no_note = 'Ignore Note corrections'.
shrtnam = 'Shorten object type description'.
but2 = 'Choose file...'.
sel_t = 'Step 1 - Select objects types that shall be included/excluded from statistic'.
obj_seli = 'Objects included'.
obj_sele = 'Objects excluded'.
p1 = '/SAP'.
START-OF-SELECTION.
"Remove all corrections from object processing
s_obj_e-sign = 'I'.
s_obj_e-option = 'EQ'.
s_obj_e-low = 'CORR'.
APPEND s_obj_e.
CALL FUNCTION 'TR_SYS_PARAMS'
IMPORTING
systemname = system_name
systemtype = system_type.
gv_sysk_pattern(3) = system_name(3).
gv_sysk_pattern+3(2) = 'K*'.
"Read texts of object list headings
CALL FUNCTION 'TR_OBJECT_TABLE'
TABLES
wt_object_text = gt_object_texts.
SORT gt_object_texts BY pgmid object.
ls_selection-trkorrpattern = gv_sysk_pattern.
ls_selection-reqfunctions = 'CDEFGKMOPQRSTWX'. "all types of requests.
ls_selection-reqstatus = 'DLP'.
CALL FUNCTION 'TRINT_SELECT_REQUESTS'
EXPORTING
is_selection = ls_selection
iv_complete_projects = 'X'
iv_via_selscreen = 'X'
is_popup = ls_popup
iv_title = 'Step 2 - Select Requests to collect statistic'
IMPORTING
et_requests = lt_request_headers
CHANGING
cs_ranges = ls_ranges
EXCEPTIONS
action_aborted_by_user = 1.
IF sy-subrc = 1.
MESSAGE e653(tk) RAISING action_aborted_by_user.
ENDIF.
"Now complete the projects (we didn't allow TRINT_SELECT_REQUESTS
"to do this due to the highlighting)
CALL FUNCTION 'TRINT_FOLLOW_STRKORR_FORWARD'
CHANGING
ct_requests = lt_request_headers.
CALL FUNCTION 'TRINT_FOLLOW_STRKORR_BACKWARD'
CHANGING
ct_requests = lt_request_headers.
"stop processing if nothing selected
CHECK lt_request_headers[] IS NOT INITIAL.
"prepare objects needed for XML creation
"create iXML instance
lo_ixml = cl_ixml=>create( ).
"get XML document
lo_xml_doc = lo_ixml->create_document( ).
lo_streamfactory = lo_ixml->create_stream_factory( ).
"if soure file contains XML and data shall be merged -- read source file
IF f1 IS NOT INITIAL AND c1 IS NOT INITIAL.
"upload a file from the client's workstation
CALL FUNCTION 'WS_UPLOAD'
EXPORTING
filename = f1
filetype = 'BIN'
IMPORTING
filelength = lv_xml_size
TABLES
data_tab = lt_xml_table
EXCEPTIONS
OTHERS = 11.
IF sy-subrc IS NOT INITIAL.
WRITE: / 'Failed to load file'.
RETURN.
ENDIF.
lo_istream = lo_streamfactory->create_istream_itable( table = lt_xml_table
size = lv_xml_size ).
"create the iXML parser:
CALL METHOD lo_ixml->create_parser
EXPORTING
stream_factory = lo_streamfactory
istream = lo_istream
document = lo_xml_doc
RECEIVING
rval = lo_parser.
"parse document & check if error happened
IF lo_parser->parse( ) NE 0.
IF lo_parser->num_errors( ) NE 0.
count = lo_parser->num_errors( ).
WRITE: / 'During XML parsing ', count, ' parse errors have occured:'.
index = 0.
WHILE index < count.
parseerror = lo_parser->get_error( index = index ).
line = parseerror->get_line( ).
column = parseerror->get_column( ).
str = parseerror->get_reason( ).
WRITE: / 'Line: ', line, ' Column: ', column, ' ', str.
index = index + 1.
ENDWHILE.
ENDIF.
RETURN.
ENDIF.
"get log node for processing:
lo_root = lo_xml_doc->find_from_name( name = 'log' ).
IF lo_root IS NOT BOUND.
WRITE: / 'Cannot find <log> node in XML file.'.
RETURN.
ENDIF.
ELSE.
"create new XML document
"add carriage return at the beginning of document <--- required by Gource's implementation of XML log parsing
lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
lo_xml_doc->append_child( lo_comment ).
"create root element:
lo_root = lo_xml_doc->create_element( name = 'log' ).
lo_xml_doc->append_child( lo_root ).
ENDIF.
"find first logentry node for processing:
lo_filter1 = lo_xml_doc->create_filter_name( name = 'log' ).
"and set it as filter for parent node:
lo_filter2 = lo_xml_doc->create_filter_parent( lo_filter1 ).
"find nodes
lo_filter3 = lo_xml_doc->create_filter_name( name = 'logentry' ).
"combine filters together
lo_filter4 = lo_xml_doc->create_filter_and( filter1 = lo_filter2 filter2 = lo_filter3 ).
"iterate over selected nodes:
lo_iterator = lo_xml_doc->create_iterator_filtered( lo_filter4 ).
lo_node_pointer = lo_iterator->get_next( ).
IF lo_node_pointer IS BOUND.
"read timestamp of first logentry as we shall 'merge' SAP changes into currently existing change log
lo_element ?= lo_node_pointer.
FREE lo_new_element.
lo_new_element = lo_element->find_from_name( name = 'date' depth = 1 ).
IF lo_new_element IS BOUND.
lv_value = lo_new_element->get_value( ).
ELSE.
WRITE: / 'Do not found <date> tag inside <logentry>'.
RETURN.
ENDIF.
"convert value into date and time:
TRY.
CALL METHOD cl_gdt_conversion=>date_time_inbound
EXPORTING
im_value = lv_value
IMPORTING
ex_value_short = lv_pointer_tstm.
CATCH cx_gdt_conversion .
WRITE: / 'Failed to convert into time value ', lv_value.
RETURN.
ENDTRY.
ELSE.
"set pointer to comment which will recieve all data
"since no logentry found - insert all nodes just above this comment node:
lo_comment = lo_xml_doc->create_comment( 'Generated by Artem Ruzak' ).
lo_node_pointer ?= lo_comment.
lo_root->append_child( lo_node_pointer ).
"add carriage return before closing log tag <--- required by Gource's implementation of XML log parsing
lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
lo_root->append_child( lo_comment ).
"set pointer date and time to maximum:
lv_pointer_tstm = '99991231235959'.
ENDIF.
SORT lt_request_headers BY as4date as4time.
lv_req_num = LINES( lt_request_headers ).
"get request objects and process them
LOOP AT lt_request_headers INTO ls_request_header.
CONVERT DATE ls_request_header-as4date TIME ls_request_header-as4time
INTO TIME STAMP lv_tstm TIME ZONE sy-zonlo.
"check at what place change shall be added:
IF lv_pointer_tstm < lv_tstm.
"find next changerequest that is higher that current one
WHILE lv_pointer_tstm < lv_tstm.
lo_node_pointer = lo_iterator->get_next( ).
IF lo_node_pointer IS BOUND.
"read timestamp of next logentry
FREE: lo_element, lo_new_element.
lo_element ?= lo_node_pointer.
lo_new_element = lo_element->find_from_name( name = 'date' depth = 1 ).
IF lo_new_element IS BOUND.
lv_value = lo_new_element->get_value( ).
ELSE.
WRITE: / 'Do not found <date> tag inside <logentry>'.
RETURN.
ENDIF.
"convert value into date and time:
TRY.
CALL METHOD cl_gdt_conversion=>date_time_inbound
EXPORTING
im_value = lv_value
IMPORTING
ex_value_short = lv_pointer_tstm.
CATCH cx_gdt_conversion .
WRITE: / 'Failed to convert into time value ', lv_value.
RETURN.
ENDTRY.
ELSE.
"set pointer to comment which will recieve all data
"since no logentry found - insert all nodes just above this comment node:
lo_comment = lo_xml_doc->create_comment( 'Generated by Artem Ruzak' ).
lo_node_pointer ?= lo_comment.
lo_root->append_child( lo_node_pointer ).
"set pointer date and time to maximum:
lv_pointer_tstm = '99991231235959'.
ENDIF.
ENDWHILE.
ENDIF.
"set progress indicator
lv_perc_tmp = ( sy-tabix / lv_req_num ) * 100.
IF TRUNC( lv_perc_tmp ) > lv_perc.
lv_perc = lv_perc_tmp.
MOVE lv_req_num TO lv_value.
MOVE lv_perc TO lv_value2.
CONCATENATE lv_value2 '% out of ' lv_value ' requests' INTO lv_value2.
CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
EXPORTING
percentage = lv_perc
text = lv_value2.
ENDIF.
REFRESH: lt_objects.
ls_request-h = ls_request_header.
CALL FUNCTION 'TRINT_READ_REQUEST'
EXPORTING
iv_read_objs_keys = 'X'
CHANGING
cs_request = ls_request
EXCEPTIONS
OTHERS = 0.
CHECK sy-subrc IS INITIAL.
APPEND LINES OF ls_request-objects TO lt_objects.
CHECK lt_objects[] IS NOT INITIAL.
"ignore corrections with notes
IF c2_note IS NOT INITIAL.
READ TABLE ls_request-objects WITH KEY object = 'NOTE'
TRANSPORTING NO FIELDS.
IF sy-subrc IS INITIAL.
"this transport contains corrections -- skip it:
CONTINUE.
ENDIF.
ENDIF.
"create log element:
lo_element = lo_xml_doc->create_element( name = 'logentry' ).
"set request number
lv_value = ls_request_header-trkorr.
lo_element->set_attribute( name = 'revision' value = lv_value ).
"add info about user
lo_new_element = lo_xml_doc->create_element( name = 'author' ).
lv_uname = ls_request_header-as4user.
CLEAR ls_uaddr.
CALL FUNCTION 'BAPI_USER_GET_DETAIL'
EXPORTING
username = lv_uname
cache_results = 'X'
IMPORTING
address = ls_uaddr
TABLES
return = lt_bapiret.
IF ls_uaddr-firstname IS INITIAL AND ls_uaddr-lastname IS INITIAL.
lv_value = ls_request_header-as4user.
ELSE.
CONCATENATE ls_uaddr-firstname ls_uaddr-lastname INTO lv_value SEPARATED BY space.
ENDIF.
lo_text = lo_xml_doc->create_text( lv_value ).
lo_new_element->append_child( lo_text ).
lo_element->append_child( lo_new_element ).
"add info about request creation date and time
lo_new_element = lo_xml_doc->create_element( name = 'date' ).
TRY.
CALL METHOD cl_gdt_conversion=>date_time_outbound
EXPORTING
im_date = ls_request_header-as4date
im_time = ls_request_header-as4time
IMPORTING
ex_value = lv_value.
CATCH cx_gdt_conversion .
CONTINUE.
ENDTRY.
lo_text = lo_xml_doc->create_text( lv_value ).
lo_new_element->append_child( lo_text ).
lo_element->append_child( lo_new_element ).
lo_new_element = lo_xml_doc->create_element( name = 'paths' ).
LOOP AT lt_objects INTO ls_objects.
"check that this object shall be included:
CHECK ls_objects-object IN s_obj_i.
"check that this object is not in exclude list
CHECK ls_objects-object NOT IN s_obj_e.
lo_path_element = lo_xml_doc->create_element( name = 'path' ).
"<path
" kind="file"
" action="A">/temp/NPQFM/FacilityManagement.launch</path>
lo_path_element->set_attribute( name = 'kind' value = 'file' ).
lo_path_element->set_attribute( name = 'action' value = 'M' ).
"build path:
lv_value = p1.
"request type
CALL FUNCTION 'TR_REQUEST_TYPE_TEXT'
EXPORTING
iv_request_type = ls_request_header-trfunction
IMPORTING
ev_request_type_text_long = lv_func_desc.
IF lv_func_desc IS NOT INITIAL.
REPLACE ALL OCCURRENCES OF REGEX '[#!:/]{1,100}' IN lv_func_desc WITH ' '.
CONCATENATE lv_value '/' lv_func_desc INTO lv_value.
ENDIF.
"add object type
READ TABLE gt_object_texts INTO ls_object_texts
WITH KEY pgmid = ls_objects-pgmid
object = ls_objects-object BINARY SEARCH.
IF sy-subrc IS INITIAL.
REPLACE ALL OCCURRENCES OF REGEX '[#!:/]{1,100}' IN ls_object_texts-text WITH ' '.
CONCATENATE lv_value '/' ls_object_texts-text INTO lv_value.
ENDIF.
"split by object-subobject relation
SPLIT ls_objects-obj_name AT space INTO lv_obj_name lv_subobj_name.
"add object itself:
REFRESH lt_name.
REPLACE ALL OCCURRENCES OF REGEX '[ #!]{1,100}' IN lv_obj_name WITH '/'.
SPLIT lv_obj_name AT '/' INTO TABLE lt_name.
LOOP AT lt_name INTO lv_name WHERE table_line IS NOT INITIAL.
CONCATENATE lv_value '/' lv_name INTO lv_value.
ENDLOOP.
"add object itself:
REFRESH lt_name.
REPLACE ALL OCCURRENCES OF REGEX '[ #!]{1,100}' IN lv_subobj_name WITH '/'.
SPLIT lv_subobj_name AT '/' INTO TABLE lt_name.
LOOP AT lt_name INTO lv_name WHERE table_line IS NOT INITIAL.
CONCATENATE lv_value '/' lv_name INTO lv_value.
ENDLOOP.
"as well add object type as object extension
READ TABLE gt_object_texts INTO ls_object_texts
WITH KEY pgmid = ls_objects-pgmid
object = ls_objects-object BINARY SEARCH.
IF sy-subrc IS INITIAL.
REPLACE ALL OCCURRENCES OF REGEX '[#!:/]{1,100}' IN ls_object_texts-text WITH ' '.
IF c3_shnam IS INITIAL.
CONCATENATE lv_value '.SAP ' ls_object_texts-text INTO lv_value.
ELSE.
"shorten object type descriptions to 8 letters, omit spaces:
lv_short_name_len = 0.
CLEAR lv_short_name.
SPLIT ls_object_texts-text AT space INTO TABLE lt_obj_type.
LOOP AT lt_obj_type INTO lv_obj_type.
IF sy-tabix = 1.
"add perfix SAP:
IF lv_obj_type NS 'SAP'.
CONCATENATE 'SAP' lv_obj_type INTO lv_obj_type.
ENDIF.
ENDIF.
"Make first letter CAPITAL
"TRANSLATE lv_obj_type(1) TO UPPER CASE.
CONCATENATE lv_short_name lv_obj_type INTO lv_short_name.
lv_short_name_len = lv_short_name_len + STRLEN( lv_obj_type ).
"exit if we got enough letters:
IF lv_short_name_len > 8.
EXIT.
ENDIF.
ENDLOOP.
CONCATENATE lv_value '.' lv_short_name INTO lv_value.
ENDIF.
ENDIF.
lo_text = lo_xml_doc->create_text( lv_value ).
lo_path_element->append_child( lo_text ).
lo_new_element->append_child( lo_path_element ).
ENDLOOP.
"add <paths>
lo_element->append_child( lo_new_element ).
"add carriage return after <paths> <--- required by Gource's implementation of XML log parsing
lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
lo_element->append_child( lo_comment ).
"add <msg> if exist
IF ls_request_header-as4text IS NOT INITIAL.
lo_new_element = lo_xml_doc->create_element( name = 'msg' ).
lv_value = ls_request_header-as4text.
lo_text = lo_xml_doc->create_text( lv_value ).
lo_new_element->append_child( lo_text ).
lo_element->append_child( lo_new_element ).
ENDIF.
"add carriage return befor closing log entry <--- required by Gource's implementation of XML log parsing
lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
lo_element->append_child( lo_comment ).
"insert new log entry on proper place
lo_root->insert_child( new_child = lo_element ref_child = lo_node_pointer ).
"add carriage return after new logentry <--- required by Gource's implementation of XML log parsing
lo_comment = lo_xml_doc->create_comment( '#CR-LF#' ).
lo_root->insert_child( new_child = lo_comment ref_child = lo_node_pointer ).
ENDLOOP.
"render XML and show on page
"create output stream
lo_ostream = lo_streamfactory->create_ostream_cstring( lv_xml_string ).
"render document
lo_renderer = lo_ixml->create_renderer( ostream = lo_ostream document = lo_xml_doc ).
lo_renderer->render( ).
"use only the first xml_size bytes of the xml table!!
lv_xml_size = lo_ostream->get_num_written( ).
"replace all <!--#CL-LF#--> with line breaks
REPLACE ALL OCCURRENCES OF '<!--#CR-LF#-->' IN lv_xml_string WITH cl_abap_char_utilities=>cr_lf.
"remove BOM
SHIFT lv_xml_string BY 1 PLACES LEFT IN CHARACTER MODE.
"change encoding
TRY .
lo_conv = cl_abap_conv_out_ce=>create( encoding = '1100' endian = 'B' ). "iso_8859_1
lo_conv->convert( EXPORTING data = lv_xml_string IMPORTING buffer = lv_xml_xstring ).
CATCH cx_sy_conversion_codepage.
"move it as it is:
CALL FUNCTION 'SCMS_STRING_TO_XSTRING'
EXPORTING
text = lv_xml_string
IMPORTING
buffer = lv_xml_xstring
EXCEPTIONS
failed = 1
OTHERS = 2.
ENDTRY.
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = lv_xml_xstring
IMPORTING
output_length = lv_xml_size
TABLES
binary_tab = lt_xml_table.
"store on disk
CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
EXPORTING
percentage = 1
text = 'Store on disk...'.
lv_filename = f1.
CALL FUNCTION 'GUI_DOWNLOAD'
EXPORTING
bin_filesize = lv_xml_size
filename = lv_filename
filetype = 'BIN'
codepage = '4110' "cp-1250
replacement = space
show_transfer_status = abap_true
TABLES
data_tab = lt_xml_table
EXCEPTIONS
file_write_error = 1
no_batch = 2
gui_refuse_filetransfer = 3
invalid_type = 4
no_authority = 5
unknown_error = 6
header_not_allowed = 7
separator_not_allowed = 8
filesize_not_allowed = 9
header_too_long = 10
dp_error_create = 11
dp_error_send = 12
dp_error_write = 13
unknown_dp_error = 14
access_denied = 15
dp_out_of_memory = 16
disk_full = 17
dp_timeout = 18
file_not_found = 19
dataprovider_exception = 20
control_flush_error = 21
OTHERS = 22.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
EXPORTING
percentage = 100
text = 'Finished.'.
Nice job with the code. I googled Gource visualization. I still really don't know what it does or what I could use it for.
Can you give me an example of what it was used for? Or was it just for fun?
Michelle
Hello Michelle,
I positively surpised how incrediable sharp-eyed are you 🙂
This blog was ment to hold only source code (hence is posted in private space) as preparation to another blog on how to use Gource for visualization of SAP projects by exporting logs from SAP transport system.
I hope to finish it by end of the day (bewteen all the usual work I shall do) so you will get also directions how to make your own video from it.
As sneak preview - here what you shall be able to get at the end 😉 http://youtu.be/l21awbB3Uoo
Best regards, Artem
Artem,
The result looks great! I can't wait for the blog. It should be interesting.
Hope to read it soon!
Michelle
Hello Michelle,
as promised - here is the blog post -
SAP code visualization - make a cool video of your project evolution
Hi Artem,
I just wanted to write a program that generates an input for Gource. Fortunately, I googled before and I found this blog. So I have less work. I have 3 questions/comments:
1. Why did you decide to generate complex XML file instead of a simple CSV file?
2. Have you though about reusing Code Inspector functionality. You can define various sets of objects in CI and there is FM SCI_GET_INSPECTION_PLAIN_LIST that gives a list of objects.
3. Splitting code into routines would increase readability of code. People should stop writing these spaghetti codes even in throw away programs.
Thanks for your effort.
Hello Martin,
glad to hear you found this code helpful.
To your questions:
1. I used XML in order to have some better readability of data which helps a lot during testing. Also another reason for me was that I am integrating that code with logs generated from SVN repository (as part of our project is on Java) and since SVN gives me back XML version I just need to provide SAP logs in same format
2. Well, I have little knowledge about code inspector lists and used more familiar to me transport system as way to select objects relevant for development. Also becouse you want to see not only changes to objects that exist now but to ones that were created in past abd were also deleted already
3. For the p.3 you are absolutely right - I should make code more nicer before posting it to community but this is a matter that is always pushed awayof ;(
If you like to improve the code or add an option for pulling data set from code inspector - go ahead. I am also curious to see what you have generated out of it. Please share a link later for myself and others.
Best regards, Artem
Hi,
I haven't run it yet and I am not sure when I will have some time. But I just noticed that you use transports for your selection. I would like to use a different approach. I would like to search by development objects first and then find corresponding transport for selected period. That's why I proposed to reuse code from code inspector. It allows you to define objects and then use them for transport selection. So I might write a new program with some pieces from you if you don't mind.
Cheers
You are welcome! The stright forward way to do what you want with possibly some manual work coudl be usage of SE03 transaction for searching of transports by objects.
Best regards, artem