azsaprfc 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azsaprfc-0.0.1/PKG-INFO +6 -0
- azsaprfc-0.0.1/azsaprfc/__init__.py +1 -0
- azsaprfc-0.0.1/azsaprfc/saprfc.py +767 -0
- azsaprfc-0.0.1/azsaprfc.egg-info/PKG-INFO +6 -0
- azsaprfc-0.0.1/azsaprfc.egg-info/SOURCES.txt +7 -0
- azsaprfc-0.0.1/azsaprfc.egg-info/dependency_links.txt +1 -0
- azsaprfc-0.0.1/azsaprfc.egg-info/top_level.txt +1 -0
- azsaprfc-0.0.1/setup.cfg +4 -0
- azsaprfc-0.0.1/setup.py +10 -0
azsaprfc-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import pyrfc
|
|
5
|
+
import sys
|
|
6
|
+
import pandas
|
|
7
|
+
from dataclasses import dataclass,field,asdict
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class ServerParameters:
|
|
11
|
+
hostname:str
|
|
12
|
+
client:str
|
|
13
|
+
user:str
|
|
14
|
+
password:str
|
|
15
|
+
sysnr:str
|
|
16
|
+
language:str
|
|
17
|
+
router:str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SapServer:
|
|
21
|
+
'''SAP RFC 连接服务器的相关基类'''
|
|
22
|
+
# 连接参数样例
|
|
23
|
+
CONNECT_PARAMETERS = {
|
|
24
|
+
'ASHOST': '', # 'XXX.XXX.XX.XX'
|
|
25
|
+
'CLIENT': '', # '800'
|
|
26
|
+
'SYSNR': '', # '00'
|
|
27
|
+
'USER': '',
|
|
28
|
+
'PASSWD': '',
|
|
29
|
+
'LANG': 'ZH',
|
|
30
|
+
'SAPROUTER': '', # '/H/XXX.XX.XX.XX/H/XXX.XX.XXX.XXX/H/'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def __init__(self, hostname, client, user='', password='', sysnr='00', language='ZH', router='') -> None:
|
|
34
|
+
self.conParameters = {
|
|
35
|
+
'ASHOST': hostname,
|
|
36
|
+
'CLIENT': client, # '800'
|
|
37
|
+
'SYSNR': sysnr, # '00'
|
|
38
|
+
'USER': user,
|
|
39
|
+
'PASSWD': password,
|
|
40
|
+
'LANG': language,
|
|
41
|
+
'SAPROUTER': router,
|
|
42
|
+
}
|
|
43
|
+
self.sapObject = None
|
|
44
|
+
|
|
45
|
+
def connect(self) -> bool:
|
|
46
|
+
'''调用RFC前需要调用此方法'''
|
|
47
|
+
# 连接SAP
|
|
48
|
+
self.sapObject = pyrfc.Connection(**self.conParameters)
|
|
49
|
+
if self.sapObject:
|
|
50
|
+
if self.sapObject.alive:
|
|
51
|
+
return True
|
|
52
|
+
else:
|
|
53
|
+
return False
|
|
54
|
+
else:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
def disconnect(self):
|
|
58
|
+
'''调用RFC后调用此方法断开连接'''
|
|
59
|
+
if self.sapObject:
|
|
60
|
+
if self.sapObject.alive:
|
|
61
|
+
self.sapObject.close
|
|
62
|
+
|
|
63
|
+
def callRFC(self, functionName, **parameters):
|
|
64
|
+
""" 调用RFC函数
|
|
65
|
+
参数说明:
|
|
66
|
+
:functionName: RFC函数名称,如:ZFM_RTR_GET_MESSAGE
|
|
67
|
+
:parameters: 字典形式
|
|
68
|
+
调用样例:
|
|
69
|
+
parameters = {'IM_MSG' : sqlStatement,
|
|
70
|
+
'IM_SOURCE': sourcelist }
|
|
71
|
+
functionName = 'ZFM_RTR_GET_MESSAGE'
|
|
72
|
+
resultList = []
|
|
73
|
+
try:
|
|
74
|
+
self.connect()
|
|
75
|
+
returnResult = self.callRFC(functionName,**parameters)
|
|
76
|
+
self.disconnect()
|
|
77
|
+
resultBinaryString = returnResult['EV_RESULT']
|
|
78
|
+
resultRows = returnResult['EV_ROWS']
|
|
79
|
+
returnMesage = returnResult['EV_MESSAGE']
|
|
80
|
+
except Exception as e:
|
|
81
|
+
str(e)
|
|
82
|
+
finally:
|
|
83
|
+
self.disconnect()
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
result = self.sapObject.call(functionName, **parameters)
|
|
87
|
+
return result
|
|
88
|
+
except Exception as e:
|
|
89
|
+
raise e
|
|
90
|
+
|
|
91
|
+
# call function:RFC_READ_TABLE
|
|
92
|
+
def readTable(self, tableName, conds: list = [], fields: list = [], DELIMITER='|'):
|
|
93
|
+
'''调用READ_TABLE函数获取表内容
|
|
94
|
+
|
|
95
|
+
如:readTable('SPFLI',[{"TEXT": "COUNTRYFR EQ 'US' "} ],['CARRID','CITYFROM'])
|
|
96
|
+
'''
|
|
97
|
+
parameters = {'QUERY_TABLE': tableName,
|
|
98
|
+
'DELIMITER': DELIMITER,
|
|
99
|
+
'OPTIONS': conds,
|
|
100
|
+
'FIELDS': fields,
|
|
101
|
+
}
|
|
102
|
+
functionName = 'RFC_READ_TABLE'
|
|
103
|
+
try:
|
|
104
|
+
self.connect()
|
|
105
|
+
returnResult = self.callRFC(functionName, **parameters)
|
|
106
|
+
self.disconnect()
|
|
107
|
+
fieldsList = returnResult['FIELDS']
|
|
108
|
+
fieldsnameList = []
|
|
109
|
+
resultData = []
|
|
110
|
+
for field in fieldsList:
|
|
111
|
+
fieldsnameList.append(field.get('FIELDNAME'))
|
|
112
|
+
for dat in returnResult['DATA']:
|
|
113
|
+
waList = dat['WA'].split(DELIMITER)
|
|
114
|
+
wa1 = [wa.strip() for wa in waList] # 删除文本空格
|
|
115
|
+
rowDict = dict(zip(fieldsnameList, wa1))
|
|
116
|
+
resultData.append(rowDict)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
resultData.append({'info': str(e)})
|
|
119
|
+
finally:
|
|
120
|
+
self.disconnect()
|
|
121
|
+
return resultData
|
|
122
|
+
|
|
123
|
+
# import pandas
|
|
124
|
+
# sap.readTable('excelfile.xls','SPFLI',[{"TEXT": "COUNTRYFR EQ 'US' "} ],['CARRID','CITYFROM'])
|
|
125
|
+
def readTableToEXCEL(self,filename,tableName,conds:list = [],fields:list=[],DELIMITER = '|'):
|
|
126
|
+
fullfilename = filename +'.xlsx'
|
|
127
|
+
parameters = {'QUERY_TABLE':tableName,
|
|
128
|
+
'DELIMITER':DELIMITER,
|
|
129
|
+
'OPTIONS':conds,
|
|
130
|
+
'FIELDS':fields,
|
|
131
|
+
}
|
|
132
|
+
functionName = 'RFC_READ_TABLE'
|
|
133
|
+
returnResult = self.callRFC(functionName,**parameters)
|
|
134
|
+
result = pandas.DataFrame([[x.strip() for x in returnResult['DATA'][n]['WA'].split(DELIMITER)] for n in range(len(returnResult['DATA']))],
|
|
135
|
+
columns=[x['FIELDNAME'] for x in returnResult['FIELDS']] )
|
|
136
|
+
result.to_excel(fullfilename,index=False)
|
|
137
|
+
|
|
138
|
+
def restExecuteSql(self, sqlStatement):
|
|
139
|
+
'''通过ADT SQL 执行SQL 查询语句
|
|
140
|
+
|
|
141
|
+
sqlStatement:SQL 语句
|
|
142
|
+
有ADT 开发权限
|
|
143
|
+
call function:SADT_REST_RFC_ENDPOINT
|
|
144
|
+
'''
|
|
145
|
+
import xml.etree.ElementTree as ET
|
|
146
|
+
request_line = {'METHOD': 'POST',
|
|
147
|
+
'URI': '/sap/bc/adt/datapreview/freestyle?rowNumber=100&dataAging=true',
|
|
148
|
+
'VERSION': 'HTTP/1.1',
|
|
149
|
+
}
|
|
150
|
+
headers = [{'NAME': 'Content-Type',
|
|
151
|
+
'VALUE': 'text/plain',
|
|
152
|
+
},
|
|
153
|
+
{'NAME': 'Accept',
|
|
154
|
+
'VALUE': 'application/xml, application/vnd.sap.adt.datapreview.table.v1+xml',
|
|
155
|
+
},
|
|
156
|
+
{'NAME': 'User-Agent',
|
|
157
|
+
'VALUE': 'Eclipse/4.28.0.v20230605-0440 (win32; x86_64; Java 17.0.7) ADT/3.36.0 (devedition)',
|
|
158
|
+
},
|
|
159
|
+
{'NAME': 'X-sap-adt-profiling',
|
|
160
|
+
'VALUE': 'server-time',
|
|
161
|
+
},
|
|
162
|
+
]
|
|
163
|
+
request = {'REQUEST_LINE': request_line,
|
|
164
|
+
'HEADER_FIELDS': headers,
|
|
165
|
+
'MESSAGE_BODY': bytes(sqlStatement, 'utf-8'),
|
|
166
|
+
}
|
|
167
|
+
parameters = {'REQUEST': request}
|
|
168
|
+
functionName = 'SADT_REST_RFC_ENDPOINT'
|
|
169
|
+
try:
|
|
170
|
+
self.connect()
|
|
171
|
+
returnResult = self.callRFC(functionName, **parameters)
|
|
172
|
+
self.disconnect()
|
|
173
|
+
response = returnResult['RESPONSE']
|
|
174
|
+
body = response['MESSAGE_BODY']
|
|
175
|
+
bodyString = str(body, encoding='utf8')
|
|
176
|
+
# xlm to list
|
|
177
|
+
content = ET.fromstring(bodyString)
|
|
178
|
+
root = content
|
|
179
|
+
NS = {"dataPreview": r"http://www.sap.com/adt/dataPreview"}
|
|
180
|
+
resultList = []
|
|
181
|
+
resultRow = {}
|
|
182
|
+
columnData = []
|
|
183
|
+
rowCount = 0
|
|
184
|
+
columCount = 0
|
|
185
|
+
# print(root.attrib['xmlns:dataPreview']) #.get('dataPreview'))
|
|
186
|
+
for column in root.findall('dataPreview:columns', namespaces=NS):
|
|
187
|
+
columCount += 1
|
|
188
|
+
col = {'name': None,
|
|
189
|
+
'data': []}
|
|
190
|
+
for field in column.findall('dataPreview:metadata', namespaces=NS):
|
|
191
|
+
col['name'] = field.attrib.get(
|
|
192
|
+
'{'+NS['dataPreview']+'}description')
|
|
193
|
+
|
|
194
|
+
# print(field.attrib.get('{'+NS['dataPreview']+'}description'))
|
|
195
|
+
for dataset in column.findall('dataPreview:dataSet', namespaces=NS):
|
|
196
|
+
for node in dataset.iter():
|
|
197
|
+
col['data'].append(node.text)
|
|
198
|
+
if columCount == 1:
|
|
199
|
+
rowCount += 1
|
|
200
|
+
columnData.append(col)
|
|
201
|
+
|
|
202
|
+
for i in range(rowCount):
|
|
203
|
+
resultRow = {}
|
|
204
|
+
for dat in columnData:
|
|
205
|
+
resultRow[dat.get('name')] = dat.get('data')[i]
|
|
206
|
+
resultList.append(resultRow)
|
|
207
|
+
except Exception as e:
|
|
208
|
+
resultList.append({'info': str(e)})
|
|
209
|
+
finally:
|
|
210
|
+
self.disconnect()
|
|
211
|
+
return resultList
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def restReadSourceBatch(self,filePath,objList:list):
|
|
215
|
+
"""_summary_
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
filePath (string): 路径
|
|
219
|
+
objList (list): 对象列表 'objType' : CLASS,PROG,FUNC,TABL
|
|
220
|
+
objList = [ {'objType':'CLASS', 'objName':'ZTEST'},
|
|
221
|
+
{'objType':'PROG', 'objName':'YTEST001'},
|
|
222
|
+
]
|
|
223
|
+
"""
|
|
224
|
+
resultList = []
|
|
225
|
+
# contList = [
|
|
226
|
+
# # {'objType':'CLASS', #CLASS,PROG,FUNC,TABL
|
|
227
|
+
# # 'objName':'ZTEST',
|
|
228
|
+
# # },
|
|
229
|
+
# {'objType':'PROG',
|
|
230
|
+
# 'objName':'YTEST001',
|
|
231
|
+
# },
|
|
232
|
+
# ]
|
|
233
|
+
# paraServer = ConnParams.S4D
|
|
234
|
+
# sapServer = SapServer(**asdict(paraServer))
|
|
235
|
+
if objList == None:
|
|
236
|
+
print('objList is None')
|
|
237
|
+
return None
|
|
238
|
+
for cnt in objList:
|
|
239
|
+
resultDict={}
|
|
240
|
+
rest = self.restReadSource(cnt.get('objType'),cnt.get('objName'))
|
|
241
|
+
fullFile = os.path.join(filePath,cnt.get('objName')+'.txt')
|
|
242
|
+
with open(fullFile,'w',encoding='UTF-8') as f:
|
|
243
|
+
f.write(rest)
|
|
244
|
+
resultDict = {
|
|
245
|
+
'objType' : cnt.get('objType'),
|
|
246
|
+
'objName': cnt.get('objName'),
|
|
247
|
+
'fileName':fullFile,
|
|
248
|
+
}
|
|
249
|
+
resultList.append(resultDict)
|
|
250
|
+
print( f'{ fullFile },Done!')
|
|
251
|
+
return resultList
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def restReadSource(self, objType,objName):
|
|
255
|
+
ulr = None
|
|
256
|
+
urltxtsymbol=None
|
|
257
|
+
urltxtselection=None
|
|
258
|
+
retString = None
|
|
259
|
+
if objName == '':
|
|
260
|
+
return None
|
|
261
|
+
if objType == 'CLASS':
|
|
262
|
+
ulr = f'/sap/bc/adt/oo/classes/{objName.lower().strip()}/source/main'
|
|
263
|
+
urltxtsymbol = f'/sap/bc/adt/textelements/classes/{objName.lower().strip()}/source/symbols'
|
|
264
|
+
|
|
265
|
+
elif objType == "PROG":
|
|
266
|
+
ulr = f'/sap/bc/adt/programs/programs/{objName.lower().strip()}/source/main'
|
|
267
|
+
urltxtsymbol = f'/sap/bc/adt/textelements/programs/{objName.lower().strip()}/source/symbols'
|
|
268
|
+
urltxtselection = f'/sap/bc/adt/textelements/programs/{objName.lower().strip()}/source/selections'
|
|
269
|
+
|
|
270
|
+
elif objType == 'FUNC':
|
|
271
|
+
cond = [{"TEXT": f"FUNCNAME EQ '{objName.upper()}'"} ]
|
|
272
|
+
funcs = self.readTable('TFDIR',cond,['PNAME'])
|
|
273
|
+
funcgrp = funcs[0].get('PNAME')[4:]
|
|
274
|
+
ulr = f'/sap/bc/adt/functions/groups/{funcgrp.lower().strip()}/fmodules/{objName.lower().strip()}/source/main'
|
|
275
|
+
urltxtsymbol = f'/sap/bc/adt/textelements/functiongroups/{funcgrp.lower().strip()}/source/symbols'
|
|
276
|
+
urltxtselection = f'/sap/bc/adt/textelements/functiongroups/{funcgrp.lower().strip()}/source/selections'
|
|
277
|
+
elif objType == 'TABL':
|
|
278
|
+
retString = self.restReadStructure(objName)
|
|
279
|
+
elif objType == 'INFO':
|
|
280
|
+
retString = self.restReadObjectList(objName)
|
|
281
|
+
|
|
282
|
+
if ulr != None:
|
|
283
|
+
retString = self.restCallURL(ulr)
|
|
284
|
+
|
|
285
|
+
retString += '\n\n"=====以下是相关文本信息=====\n\n'
|
|
286
|
+
if urltxtsymbol !=None:
|
|
287
|
+
# text symbols
|
|
288
|
+
retTxtSymbol = self.restCallURL(urltxtsymbol,Accepttype='application/vnd.sap.adt.textelements.symbols.v1')
|
|
289
|
+
txtList=retTxtSymbol.split('\r')
|
|
290
|
+
retString += '\n\n"=====以下是文本元素=====\n'
|
|
291
|
+
for txt in txtList:
|
|
292
|
+
if txt and txt[:1] != '@':
|
|
293
|
+
retString += '\n' + txt
|
|
294
|
+
if urltxtselection !=None:
|
|
295
|
+
# text symbols
|
|
296
|
+
retTxtSymbol = self.restCallURL(urltxtselection,Accepttype='application/vnd.sap.adt.textelements.selections.v1')
|
|
297
|
+
txtList=retTxtSymbol.split('\r')
|
|
298
|
+
retString += '\n\n"=====以下是Selection文本=====\n'
|
|
299
|
+
for txt in txtList:
|
|
300
|
+
if txt :
|
|
301
|
+
retString += '\n' + txt
|
|
302
|
+
return retString
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# property
|
|
306
|
+
# Accept : application/vnd.sap.adt.repository.objproperties.result.v1+xml
|
|
307
|
+
# GET /sap/bc/adt/repository/informationsystem/objectproperties/values?
|
|
308
|
+
# uri=/sap/bc/adt/functions/groups/zfg_rtr_f0007/fmodules/zfm_rtr_int_017_process&facet=package&facet=appl
|
|
309
|
+
# /sap/bc/adt/programs/programs/zrtrrf0140&facet=package&facet=appl
|
|
310
|
+
# /sap/bc/adt/oo/classes/zcl_abap_utilities&facet=package&facet=appl
|
|
311
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
|
312
|
+
# +<opr:object expandable="true" type="CLAS/OC" package="ZRTR" name="ZCL_ABAP_UTILITIES" text="工具">
|
|
313
|
+
|
|
314
|
+
# structure
|
|
315
|
+
# /sap/bc/adt/ddic/elementinfo?path=zscud_info
|
|
316
|
+
# Accept : application/vnd.sap.adt.elementinfo+xml
|
|
317
|
+
# adtcore:name="zrtrsf00100" adtcore:type="TABL/DS" adtcore:uri="/sap/bc/adt/ddic/structures/zrtrsf00100">
|
|
318
|
+
|
|
319
|
+
# abap URL list 代码
|
|
320
|
+
# DATA(lr_inst) = cl_adt_tools_core_factory=>get_instance( ).
|
|
321
|
+
# DATA(lr_urlmapper) = lr_inst->get_uri_mapper( ).
|
|
322
|
+
# " CL_ADT_URI_TEMPLATES_SHM_ROOT->BUILD_TEMPLATES
|
|
323
|
+
# DATA(type_provider) = cl_wb_registry=>get_objtype_provider( ).
|
|
324
|
+
# type_provider->get_objtypes( IMPORTING p_objtype_data = DATA(wbobjtype_data) ).
|
|
325
|
+
# LOOP AT wbobjtype_data->mt_wbobjtype ASSIGNING FIELD-SYMBOL(<type>) WHERE pgmid = 'R3TR'
|
|
326
|
+
# AND is_main_subtype_wb = abap_true.
|
|
327
|
+
# DATA ls_type TYPE wbobjtype.
|
|
328
|
+
# ls_type-objtype_tr = <type>-objecttype.
|
|
329
|
+
# ls_type-subtype_wb = <type>-subtype_wb.
|
|
330
|
+
# *<type>-description
|
|
331
|
+
# DATA(lv_type) = |{ <type>-objecttype }{ <type>-subtype_wb }|.
|
|
332
|
+
# DATA(uri) = cl_adt_tools_core_factory=>get_instance( )->get_uri_mapper( )->get_adt_object_ref_uri(
|
|
333
|
+
# name = 'ZCL_RTR_COMMON'
|
|
334
|
+
# type = ls_type ).
|
|
335
|
+
# ENDLOOP.
|
|
336
|
+
|
|
337
|
+
def restCallURL(self, url,Accepttype='text/plain'):
|
|
338
|
+
'''通过ADT 获取代码
|
|
339
|
+
有ADT 开发权限
|
|
340
|
+
call function:SADT_REST_RFC_ENDPOINT
|
|
341
|
+
url:
|
|
342
|
+
class source:/sap/bc/adt/oo/classes/{classname.lower().strip()}/source/main
|
|
343
|
+
report source: /sap/bc/adt/programs/programs/{programname.lower().strip()}/source/main source
|
|
344
|
+
report text: /sap/bc/adt/textelements/programs/{programname.lower().strip()/source/selections?version=workingArea
|
|
345
|
+
/sap/bc/adt/textelements/programs/{programname.lower().strip()/source/symbols?version=workingArea
|
|
346
|
+
funct source: /sap/bc/adt/functions/groups/{funcgrp.lower().strip()}/fmodules/{objName.lower().strip()}/source/main
|
|
347
|
+
/sap/bc/adt/ddic/tables/mara/source/main
|
|
348
|
+
'''
|
|
349
|
+
request_line = {'METHOD': 'GET',
|
|
350
|
+
'URI': url,
|
|
351
|
+
'VERSION': 'HTTP/1.1',
|
|
352
|
+
}
|
|
353
|
+
headers = [
|
|
354
|
+
|
|
355
|
+
{'NAME': 'Cache-Control',
|
|
356
|
+
'VALUE': 'no-cache',
|
|
357
|
+
},
|
|
358
|
+
{'NAME': 'Accept',
|
|
359
|
+
'VALUE': Accepttype,
|
|
360
|
+
},
|
|
361
|
+
{'NAME': 'User-Agent',
|
|
362
|
+
'VALUE': 'Eclipse/4.28.0.v20230605-0440 (win32; x86_64; Java 17.0.7) ADT/3.38.2 (devedition)',
|
|
363
|
+
},
|
|
364
|
+
{'NAME': 'X-sap-adt-profiling',
|
|
365
|
+
'VALUE': 'server-time',
|
|
366
|
+
},
|
|
367
|
+
]
|
|
368
|
+
request = {'REQUEST_LINE': request_line,
|
|
369
|
+
'HEADER_FIELDS': headers,
|
|
370
|
+
# 'MESSAGE_BODY': bytes(sqlStatement, 'utf-8'),
|
|
371
|
+
}
|
|
372
|
+
parameters = {'REQUEST': request}
|
|
373
|
+
functionName = 'SADT_REST_RFC_ENDPOINT'
|
|
374
|
+
try:
|
|
375
|
+
self.connect()
|
|
376
|
+
returnResult = self.callRFC(functionName, **parameters)
|
|
377
|
+
self.disconnect()
|
|
378
|
+
response = returnResult['RESPONSE']
|
|
379
|
+
body = response['MESSAGE_BODY']
|
|
380
|
+
# print(body)
|
|
381
|
+
bodyString1 = str(body, encoding='utf8')
|
|
382
|
+
# bodyString = str(body, encoding='utf8')
|
|
383
|
+
bodyString = str(body, encoding='utf8').replace('\n','')
|
|
384
|
+
# print(bodyString1)
|
|
385
|
+
except Exception as e:
|
|
386
|
+
pass
|
|
387
|
+
finally:
|
|
388
|
+
self.disconnect()
|
|
389
|
+
return bodyString
|
|
390
|
+
|
|
391
|
+
def restReadStructure(self,objName):
|
|
392
|
+
url = f'/sap/bc/adt/ddic/elementinfo?path={objName.lower().strip()}'
|
|
393
|
+
resultXmlString=self.restCallURL(url ,Accepttype='application/vnd.sap.adt.elementinfo+xml')
|
|
394
|
+
lines = 0
|
|
395
|
+
import xml.etree.ElementTree as ET
|
|
396
|
+
xmlroot = ET.fromstring(resultXmlString)
|
|
397
|
+
NS = {'abapsource':r'http://www.sap.com/adt/abapsource',
|
|
398
|
+
'adtcore':r'http://www.sap.com/adt/core'}
|
|
399
|
+
tabdesc = xmlroot.find("abapsource:documentation",namespaces= NS).text
|
|
400
|
+
fieldstring = f'Talble/Structure:{objName}, Description:{tabdesc}\n'
|
|
401
|
+
# tabdesc = xmlroot.find('{'+NS['abapsource']+'}documentation').text
|
|
402
|
+
resultList=[]
|
|
403
|
+
columns = xmlroot.findall('abapsource:elementInfo' , namespaces= NS )
|
|
404
|
+
for column in columns:
|
|
405
|
+
lines +=1
|
|
406
|
+
if column.attrib.get('{'+NS['adtcore']+'}type') != "TABL/DTF":
|
|
407
|
+
continue
|
|
408
|
+
field={}
|
|
409
|
+
field['fieldName'] = column.attrib.get('{'+NS['adtcore']+'}name')
|
|
410
|
+
field['fieldDesc'] = column.find('abapsource:documentation' , namespaces= NS).text
|
|
411
|
+
# field['fieldDesc'] = column.findall('abapsource:documentation' , namespaces= NS)[0].text
|
|
412
|
+
fieldinfos = column.findall('abapsource:properties' , namespaces= NS)
|
|
413
|
+
for info in fieldinfos[0].findall('abapsource:entry' , namespaces= NS):
|
|
414
|
+
if info.attrib.get('{'+NS['abapsource']+'}key') == 'ddicDataElement':
|
|
415
|
+
field['dtlElement'] = info.text
|
|
416
|
+
elif info.attrib.get('{'+NS['abapsource']+'}key') == 'ddicDataType':
|
|
417
|
+
field['dataType'] = info.text
|
|
418
|
+
elif info.attrib.get('{'+NS['abapsource']+'}key') == 'ddicLength':
|
|
419
|
+
field['dataLen'] = info.text
|
|
420
|
+
elif info.attrib.get('{'+NS['abapsource']+'}key') == 'ddicDecimals':
|
|
421
|
+
field['dataDecimals'] = info.text
|
|
422
|
+
elif info.attrib.get('{'+NS['abapsource']+'}key') == 'ddicIsKey':
|
|
423
|
+
field['isKey'] = info.text
|
|
424
|
+
elif info.attrib.get('{'+NS['abapsource']+'}key') == 'parentName':
|
|
425
|
+
field['parentName'] = info.text
|
|
426
|
+
if lines == 1:
|
|
427
|
+
fieldstring += '\n' + '\t'.join(field.keys())
|
|
428
|
+
fieldstring += '\n=====================================================\n'
|
|
429
|
+
fieldstring += '\n' + '\t'.join(field.values())
|
|
430
|
+
resultList.append(field)
|
|
431
|
+
return fieldstring
|
|
432
|
+
|
|
433
|
+
def restReadObjectList(self,objName):
|
|
434
|
+
url = f'/sap/bc/adt/repository/informationsystem/search?operation=quickSearch&query={objName.lower().strip()}*&maxResults=51'
|
|
435
|
+
resultXmlString=self.restCallURL(url ,Accepttype='application/xml')
|
|
436
|
+
lines = 0
|
|
437
|
+
import xml.etree.ElementTree as ET
|
|
438
|
+
xmlroot = ET.fromstring(resultXmlString)
|
|
439
|
+
NS = { 'adtcore':r'http://www.sap.com/adt/core'}
|
|
440
|
+
# tabdesc = xmlroot.find('{'+NS['abapsource']+'}documentation').text
|
|
441
|
+
fieldstring = f'Searching:{objName}:\n'
|
|
442
|
+
resultList=[]
|
|
443
|
+
objlists = xmlroot.findall('adtcore:objectReference' , namespaces= NS )
|
|
444
|
+
for obj in objlists:
|
|
445
|
+
lines +=1
|
|
446
|
+
result={}
|
|
447
|
+
result['name'] = obj.attrib.get('{'+NS['adtcore']+'}name')
|
|
448
|
+
result['type'] = obj.attrib.get('{'+NS['adtcore']+'}type')
|
|
449
|
+
result['description'] = obj.attrib.get('{'+NS['adtcore']+'}description')
|
|
450
|
+
result['uri'] = obj.attrib.get('{'+NS['adtcore']+'}uri')
|
|
451
|
+
result['packageName'] = obj.attrib.get('{'+NS['adtcore']+'}packageName')
|
|
452
|
+
if lines == 1:
|
|
453
|
+
fieldstring += '\n' + '\t'.join(result.keys())
|
|
454
|
+
fieldstring += '\n=====================================================\n'
|
|
455
|
+
fieldstring += '\n' + '\t'.join(result.values())
|
|
456
|
+
resultList.append(result)
|
|
457
|
+
return fieldstring
|
|
458
|
+
# return resultList
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def executeProgramDynamic(self, codeFileName,funcID, para1, para2='', para3='', para4=''):
|
|
462
|
+
'''通过函数 ECATT_REMOTE_MODULE_EXECUTE 动态执行各种语句
|
|
463
|
+
|
|
464
|
+
需要权限对象S_DEVELOP:
|
|
465
|
+
OBJTYPE:'SCAT', 'ACTVT' '16'.
|
|
466
|
+
或 'OBJTYPE' , 'ECSC' 'ACTVT' '16'.
|
|
467
|
+
执行代码在文件:sapdynamicprogram.abap
|
|
468
|
+
'''
|
|
469
|
+
|
|
470
|
+
abapsource = ''
|
|
471
|
+
resultJson = ''
|
|
472
|
+
result = {}
|
|
473
|
+
resultList = []
|
|
474
|
+
current_file_path = os.path.abspath(__file__) # 当前文件绝对路径
|
|
475
|
+
current_path = os.path.dirname(current_file_path) # 文件所在的路径
|
|
476
|
+
filenamewithpath = os.path.join(current_path, 'sapdynamicprogram.abap')
|
|
477
|
+
sourcelist = []
|
|
478
|
+
inputPara = []
|
|
479
|
+
filenamewithpath = codeFileName
|
|
480
|
+
with open(filenamewithpath, 'r', encoding='UTF-8') as f:
|
|
481
|
+
abapsource = f.readline()
|
|
482
|
+
source = {'LINE': abapsource.replace("\n", "")}
|
|
483
|
+
sourcelist.append(source)
|
|
484
|
+
while abapsource:
|
|
485
|
+
abapsource = f.readline()
|
|
486
|
+
source = {}
|
|
487
|
+
source = {'LINE': abapsource.replace("\n", "")}
|
|
488
|
+
sourcelist.append(source)
|
|
489
|
+
inputParaDict = {'FUNCTIONID': funcID,
|
|
490
|
+
'PARA1': para1,
|
|
491
|
+
'PARA2': para2,
|
|
492
|
+
'PARA3': para3,
|
|
493
|
+
'PARA4': para4,
|
|
494
|
+
}
|
|
495
|
+
inputParaJson = json.dumps(inputParaDict, ensure_ascii=False)
|
|
496
|
+
# 30长度拆分字符到List
|
|
497
|
+
inputParaList = re.findall('.{'+str(30)+'}', inputParaJson)
|
|
498
|
+
inputParaList.append(inputParaJson[(len(inputParaList)*30):])
|
|
499
|
+
|
|
500
|
+
for para in inputParaList:
|
|
501
|
+
paradict = {'FLAG': '', 'VALUE': para}
|
|
502
|
+
inputPara.append(paradict)
|
|
503
|
+
parameters = {'PROGRAM': sourcelist,
|
|
504
|
+
'ECATT_TRANSFER_DATA_CONTAINER': inputPara}
|
|
505
|
+
functionName = 'ECATT_REMOTE_MODULE_EXECUTE'
|
|
506
|
+
resultList = []
|
|
507
|
+
try:
|
|
508
|
+
self.connect()
|
|
509
|
+
returnResult = self.callRFC(functionName, **parameters)
|
|
510
|
+
self.disconnect()
|
|
511
|
+
returnMsg = returnResult.get('FUN_MESSAGE').get('MSGTEXT')
|
|
512
|
+
if returnResult.get('SY_SUBRC') == 0:
|
|
513
|
+
returnCode = '200'
|
|
514
|
+
else:
|
|
515
|
+
returnCode = '201'
|
|
516
|
+
resultTable = returnResult.get('ECATT_TRANSFER_DATA_CONTAINER')
|
|
517
|
+
for res in resultTable:
|
|
518
|
+
resultJson += res.get('VALUE')
|
|
519
|
+
print(resultJson)
|
|
520
|
+
if resultJson:
|
|
521
|
+
resultList = json.loads(resultJson)
|
|
522
|
+
result = {
|
|
523
|
+
'ResultList': resultList,
|
|
524
|
+
'Rows': 0,
|
|
525
|
+
'Code': returnCode,
|
|
526
|
+
'Message': returnMsg,
|
|
527
|
+
}
|
|
528
|
+
except Exception as e:
|
|
529
|
+
# print(str(e))
|
|
530
|
+
result = {
|
|
531
|
+
'ResultList': [],
|
|
532
|
+
'Rows': 0,
|
|
533
|
+
'Code': '201',
|
|
534
|
+
'Message': str(e),
|
|
535
|
+
}
|
|
536
|
+
finally:
|
|
537
|
+
self.disconnect()
|
|
538
|
+
return result
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def getFunctionParameters(self,imFunctionName):
|
|
542
|
+
resultList=[]
|
|
543
|
+
try:
|
|
544
|
+
self.connect()
|
|
545
|
+
func_desc = self.sapObject.get_function_description(imFunctionName)
|
|
546
|
+
for parameter in func_desc.parameters:
|
|
547
|
+
para = {
|
|
548
|
+
"direction":parameter["direction"],
|
|
549
|
+
"name":parameter["name"],
|
|
550
|
+
"parameter_text":parameter["parameter_text"],
|
|
551
|
+
"parameter_type":parameter["parameter_type"],
|
|
552
|
+
"nuc_length":parameter["nuc_length"],
|
|
553
|
+
"uc_length":parameter["uc_length"],
|
|
554
|
+
"decimals":parameter["decimals"],
|
|
555
|
+
"default_value":parameter["default_value"],
|
|
556
|
+
"optional":parameter["optional"],
|
|
557
|
+
"type_name":None ,
|
|
558
|
+
}
|
|
559
|
+
if parameter["type_description"] != None:
|
|
560
|
+
para['type_name'] = parameter["type_description"].name
|
|
561
|
+
resultList.append(para)
|
|
562
|
+
# print(para)
|
|
563
|
+
except:
|
|
564
|
+
pass
|
|
565
|
+
finally:
|
|
566
|
+
self.disconnect()
|
|
567
|
+
return resultList
|
|
568
|
+
|
|
569
|
+
def getFunctionParameterType(self,imFunctionName,imParameter):
|
|
570
|
+
resultDict={}
|
|
571
|
+
resultFieldList = []
|
|
572
|
+
try:
|
|
573
|
+
self.connect()
|
|
574
|
+
func_desc = self.sapObject.get_function_description(imFunctionName)
|
|
575
|
+
for parameter in func_desc.parameters:
|
|
576
|
+
if parameter.get("name") == imParameter and parameter["type_description"] != None:
|
|
577
|
+
|
|
578
|
+
for itm in parameter["type_description"].fields:
|
|
579
|
+
# print(itm)
|
|
580
|
+
fields ={
|
|
581
|
+
# 'FieldType':itm['type']
|
|
582
|
+
'name':itm['name'],
|
|
583
|
+
'field_type':itm['field_type'],
|
|
584
|
+
'nuc_offset':itm['nuc_offset'],
|
|
585
|
+
'nuc_length':itm['nuc_length'],
|
|
586
|
+
'uc_length':itm['uc_length'],
|
|
587
|
+
'uc_offset':itm['uc_offset'],
|
|
588
|
+
'decimals':itm['decimals'],
|
|
589
|
+
'type_name':itm['type_description'].name if itm['type_description'] !=None else "",
|
|
590
|
+
}
|
|
591
|
+
resultFieldList.append(fields)
|
|
592
|
+
resultDict={
|
|
593
|
+
'para_name':imParameter,
|
|
594
|
+
'type_name':parameter["type_description"].name,
|
|
595
|
+
'fields':resultFieldList
|
|
596
|
+
}
|
|
597
|
+
except:
|
|
598
|
+
pass
|
|
599
|
+
finally:
|
|
600
|
+
self.disconnect()
|
|
601
|
+
return resultDict
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def getTableStructure(self,imTableName):
|
|
605
|
+
resultList=[]
|
|
606
|
+
if imTableName == None or imTableName == "" :
|
|
607
|
+
return None
|
|
608
|
+
try:
|
|
609
|
+
self.connect()
|
|
610
|
+
parameter={
|
|
611
|
+
"TABNAME": imTableName,
|
|
612
|
+
"ALL_TYPES": "X",
|
|
613
|
+
}
|
|
614
|
+
functionName = "DDIF_FIELDINFO_GET"
|
|
615
|
+
tabstru = self.callRFC(functionName,**parameter)
|
|
616
|
+
if tabstru.get("DDOBJTYPE") == "TTYP":
|
|
617
|
+
print(tabstru.get("DFIES_WA").get("ROLLNAME"))
|
|
618
|
+
resultList = self.getTableStructure(tabstru.get("DFIES_WA").get("ROLLNAME"))
|
|
619
|
+
return resultList
|
|
620
|
+
for field in tabstru.get("DFIES_TAB"):
|
|
621
|
+
fields ={
|
|
622
|
+
'FieldName': field["FIELDNAME"],
|
|
623
|
+
'Position': field["POSITION"],
|
|
624
|
+
'DataType': field["DATATYPE"],
|
|
625
|
+
'FieldText': field["FIELDTEXT"],
|
|
626
|
+
'OffSet': field["OFFSET"],
|
|
627
|
+
'RollName': field["ROLLNAME"],
|
|
628
|
+
'Leng':field["LENG"],
|
|
629
|
+
'Decimals':field["DECIMALS"],
|
|
630
|
+
'FieldTitle': field["SCRTEXT_L"],
|
|
631
|
+
}
|
|
632
|
+
resultList.append(fields)
|
|
633
|
+
|
|
634
|
+
except:
|
|
635
|
+
pass
|
|
636
|
+
finally:
|
|
637
|
+
self.disconnect()
|
|
638
|
+
return resultList
|
|
639
|
+
|
|
640
|
+
def printTableStructure(self,imTableName):
|
|
641
|
+
resultList = self.getTableStructure(imTableName)
|
|
642
|
+
for result in resultList:
|
|
643
|
+
print(result)
|
|
644
|
+
|
|
645
|
+
def printFunctionParameterType(self,imFunctionName,imParameter):
|
|
646
|
+
resultList = self.getFunctionParameterType(imFunctionName,imParameter)
|
|
647
|
+
print(resultList['type_name'])
|
|
648
|
+
for result in resultList['fields']:
|
|
649
|
+
print(result)
|
|
650
|
+
|
|
651
|
+
def printFunctionParameters(self,imFunctionName):
|
|
652
|
+
resultList = self.getFunctionJsonParameters(imFunctionName)
|
|
653
|
+
for result in resultList:
|
|
654
|
+
print(result)
|
|
655
|
+
|
|
656
|
+
def getFunctionJsonParameters(self,imFunctionName):
|
|
657
|
+
resultDict = {}
|
|
658
|
+
funcParams = self.getFunctionJsonParameters(imFunctionName)
|
|
659
|
+
importPara = {}
|
|
660
|
+
# importPara = {fp['name']:fp.get('default_value',None) for fp in funcParams if fp['direction'] == 'RFC_IMPORT'}
|
|
661
|
+
for fp in funcParams:
|
|
662
|
+
if fp['direction'] == 'RFC_IMPORT':
|
|
663
|
+
if fp['type_description'] != None:
|
|
664
|
+
fieldsdict = {field['name']:None for field in fp['type_description']['structure']}
|
|
665
|
+
importPara.update({fp['name']:fieldsdict })
|
|
666
|
+
else:
|
|
667
|
+
importPara.update({fp['name']:fp.get('default_value',None)})
|
|
668
|
+
tablePara = {fp['name']:[{field['name']:None for field in fp['type_description']['structure']}] for fp in funcParams if fp['direction'] == 'RFC_TABLES'}
|
|
669
|
+
for dt in [importPara, tablePara]:
|
|
670
|
+
resultDict.update(dt)
|
|
671
|
+
return json.dumps(resultDict)
|
|
672
|
+
|
|
673
|
+
class SAPUILandscape:
|
|
674
|
+
def __init__(self,file_name ) :
|
|
675
|
+
import xml.etree.ElementTree as ET
|
|
676
|
+
self.content = ET.parse(file_name)
|
|
677
|
+
self.Items = []
|
|
678
|
+
self.Services = []
|
|
679
|
+
self.Routers = []
|
|
680
|
+
self.Messageservers= []
|
|
681
|
+
self.ServerList = []
|
|
682
|
+
|
|
683
|
+
import xml.etree.ElementTree as ET
|
|
684
|
+
self.content = ET.parse(file_name)
|
|
685
|
+
root = self.content.getroot()
|
|
686
|
+
# root[0].tag
|
|
687
|
+
# root[0].text
|
|
688
|
+
for item in root.iter('Item'): # root.findall('Item'):
|
|
689
|
+
itm= {
|
|
690
|
+
'uuid':item.get('uuid'),
|
|
691
|
+
'serviceid':item.get('uuid')
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
for item in root.iter('Item'): # root.findall('Item'):
|
|
696
|
+
itm= {
|
|
697
|
+
'uuid':item.get('uuid'),
|
|
698
|
+
'serviceid':item.get('uuid')
|
|
699
|
+
}
|
|
700
|
+
self.Items.append(itm)
|
|
701
|
+
|
|
702
|
+
for Route in root.iter('Router'): # root.findall('Router'):
|
|
703
|
+
dict = {
|
|
704
|
+
'uuid':Route.get('uuid'),
|
|
705
|
+
'name':Route.get('name'),
|
|
706
|
+
'description':Route.get('description'),
|
|
707
|
+
'router':Route.get('router')
|
|
708
|
+
}
|
|
709
|
+
self.Routers.append(dict)
|
|
710
|
+
|
|
711
|
+
for Service in root.iter('Service'): #root.findall('Service'):
|
|
712
|
+
dict = {
|
|
713
|
+
'type':Service.get('type'),
|
|
714
|
+
'uuid':Service.get('uuid'),
|
|
715
|
+
'name':Service.get('name'),
|
|
716
|
+
'systemid':Service.get('systemid'),
|
|
717
|
+
'msid':Service.get('msid'),
|
|
718
|
+
'server':Service.get('server'),
|
|
719
|
+
'routerid':Service.get('routerid'),
|
|
720
|
+
'dcpg':Service.get('dcpg'),
|
|
721
|
+
'sncname':Service.get('sncname'),
|
|
722
|
+
}
|
|
723
|
+
if dict['type'] == 'SAPGUI':
|
|
724
|
+
self.Services.append(dict)
|
|
725
|
+
|
|
726
|
+
for MSService in root.iter('Messageserver'): # root.findall('Messageserver'):
|
|
727
|
+
dict = {
|
|
728
|
+
'uuid':MSService.get('uuid'),
|
|
729
|
+
'name':MSService.get('name'),
|
|
730
|
+
'description':MSService.get('description'),
|
|
731
|
+
'host':MSService.get('host'),
|
|
732
|
+
'port':MSService.get('port'),
|
|
733
|
+
}
|
|
734
|
+
self.Messageservers.append(dict)
|
|
735
|
+
|
|
736
|
+
for service in self.Services:
|
|
737
|
+
dict = {
|
|
738
|
+
'name':service.get('name'),
|
|
739
|
+
'system':service.get('systemid'),
|
|
740
|
+
'systemNo':''
|
|
741
|
+
}
|
|
742
|
+
server = service.get('server')
|
|
743
|
+
if server :
|
|
744
|
+
serveraddress = server.split(':')
|
|
745
|
+
dict['serverGroup'] = serveraddress[0]
|
|
746
|
+
if len(serveraddress) > 1:
|
|
747
|
+
dict['systemNo'] = serveraddress[1][2:4]
|
|
748
|
+
if service['msid'] in [dict1.get('uuid') for dict1 in self.Messageservers ]:
|
|
749
|
+
dict1 = sorted( self.Messageservers,key = lambda dicts:dicts['uuid'] != service['msid'])[0]
|
|
750
|
+
dict['messageServer'] = dict1.get('host')
|
|
751
|
+
|
|
752
|
+
if service['routerid'] in [dict1.get('uuid') for dict1 in self.Routers ]:
|
|
753
|
+
dict1 = sorted( self.Routers,key = lambda dicts:dicts['uuid'] != service['routerid'])[0]
|
|
754
|
+
dict['router'] = dict1.get('router')
|
|
755
|
+
|
|
756
|
+
dict['dcpg'] = service.get('dcpg')
|
|
757
|
+
dict['sncname'] = service.get('sncname')
|
|
758
|
+
self.ServerList.append(dict)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
# df1 = pandas.DataFrame(self.ServerList)
|
|
762
|
+
# df1.to_excel('saspconfig.xlsx',index=False)
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
if __name__ == '__main__':
|
|
767
|
+
pass
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
azsaprfc
|
azsaprfc-0.0.1/setup.cfg
ADDED
azsaprfc-0.0.1/setup.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
setup(
|
|
3
|
+
name = 'azsaprfc',
|
|
4
|
+
version = '0.0.1',
|
|
5
|
+
author = 'AndyZhu',
|
|
6
|
+
author_email = 'andyzhu@live.com', # 作者邮箱
|
|
7
|
+
description='SAP Tools for python', # 描述
|
|
8
|
+
# packages = ['azpysaprfc'],
|
|
9
|
+
packages=find_packages(),
|
|
10
|
+
)
|