1010from . import constants as C
1111from .sap_api_common import _request , https_session
1212from .sap_id_sso import _get_sso_endpoint_meta
13+ from .sap_launchpad_software_center_download_search_fuzzy import *
1314
1415logger = logging .getLogger (__name__ )
1516
1617_HAS_DOWNLOAD_AUTHORIZATION = None
1718MAX_RETRY_TIMES = 3
1819
1920
20- def search_software_filename (name , deduplicate ):
21- """Return a single software that matched the filename
21+ def search_software_filename (name , deduplicate , search_alternatives ):
2222 """
23- search_results = _search_software (name )
24- softwares = [r for r in search_results if r ['Title' ] == name or r ['Description' ] == name ]
25- num_files = len (softwares )
26- if num_files == 0 :
27- raise ValueError (f'no result found for { name } ' )
28- elif num_files > 1 and deduplicate == '' :
29- names = [s ['Title' ] for s in softwares ]
30- raise ValueError ('more than one results were found: %s. '
23+ Execute search for SAP Software or its alternative when search_alternatives is true.
24+
25+ Args:
26+ name: The filename name to check (e.g. 'SAPCAR_1115-70006178.EXE').
27+ deduplicate: Select deduplication logic from 'first', 'last'
28+ search_alternatives: Boolean for enabling fuzzy search.
29+
30+ Returns:
31+ download_link: Download link of matched SAP Software.
32+ filename: File name of matched SAP Software.
33+ alternative_found: True if alternative search was successful.
34+ """
35+
36+ alternative_found = False
37+ software_search = _search_software (name )
38+ software_filtered = [r for r in software_search if r ['Title' ] == name or r ['Description' ] == name ]
39+
40+ files_count = len (software_filtered )
41+ if files_count == 0 :
42+ # Run fuzzy search if search_alternatives was selected
43+ if search_alternatives :
44+ software_fuzzy_found = search_software_fuzzy (name )
45+ software_fuzzy_filtered , suggested_filename = filter_fuzzy_search (software_fuzzy_found , name )
46+ if len (software_fuzzy_filtered ) == 0 :
47+ raise ValueError (f'File { name } is not available to download and has no alternatives' )
48+
49+ software_fuzzy_alternatives = software_fuzzy_filtered [0 ].get ('Title' )
50+
51+ # Search has to be filtered again, because API call can get
52+ # duplicates like 70SWPM10SP43_2-20009701.sar for SWPM10SP43_2-20009701.SAR
53+ software_search_alternatives = _search_software (software_fuzzy_alternatives )
54+ software_search_alternatives_filtered = [
55+ file for file in software_search_alternatives
56+ if file .get ('Title' , '' ).startswith (suggested_filename )
57+ ]
58+ alternatives_count = len (software_search_alternatives_filtered )
59+ if alternatives_count == 0 :
60+ raise ValueError (f'File { name } is not available to download and has no alternatives' )
61+ elif alternatives_count > 1 and deduplicate == '' :
62+ names = [s ['Title' ] for s in software_search_alternatives_filtered ]
63+ raise ValueError ('More than one results were found: %s. '
64+ 'please use the correct full filename' % names )
65+ elif alternatives_count > 1 and deduplicate == 'first' :
66+ software_found = software_search_alternatives_filtered [0 ]
67+ alternative_found = True
68+ elif alternatives_count > 1 and deduplicate == 'last' :
69+ software_found = software_search_alternatives_filtered [alternatives_count - 1 ]
70+ alternative_found = True
71+ else :
72+ software_found = software_search_alternatives_filtered [0 ]
73+ alternative_found = True
74+ else :
75+ raise ValueError (f'File { name } is not available to download. Enable "search_alternatives" to search for alternatives.' )
76+
77+ elif files_count > 1 and deduplicate == '' :
78+ names = [s ['Title' ] for s in software_filtered ]
79+ raise ValueError ('More than one results were found: %s. '
3180 'please use the correct full filename' % names )
32- elif num_files > 1 and deduplicate == 'first' :
33- software = softwares [0 ]
34- elif num_files > 1 and deduplicate == 'last' :
35- software = softwares [ num_files - 1 ]
81+ elif files_count > 1 and deduplicate == 'first' :
82+ software_found = software_filtered [0 ]
83+ elif files_count > 1 and deduplicate == 'last' :
84+ software_found = software_filtered [ files_count - 1 ]
3685 else :
37- software = softwares [0 ]
86+ software_found = software_filtered [0 ]
3887
39- download_link , filename = software ['DownloadDirectLink' ], name
40- return (download_link , filename )
88+ download_link = software_found ['DownloadDirectLink' ]
89+ filename = _get_valid_filename (software_found )
90+
91+ return (download_link , filename , alternative_found )
4192
4293
4394def download_software (download_link , filename , output_dir , retry = 0 ):
@@ -151,6 +202,7 @@ def download_software_via_legacy_api(username, password, download_link,
151202
152203
153204def _search_software (keyword ):
205+
154206 url = C .URL_SOFTWARE_CENTER_SERVICE + '/SearchResultSet'
155207 params = {
156208 'SEARCH_MAX_RESULT' : 500 ,
@@ -167,7 +219,7 @@ def _search_software(keyword):
167219 j = json .loads (res .text )
168220 results = j ['d' ]['results' ]
169221 except json .JSONDecodeError :
170- # When use has no authority to search some specified softwares ,
222+ # When use has no authority to search some specified files ,
171223 # it will return non-json response, which is actually expected.
172224 # So just return an empty list.
173225 logger .warning ('Non-JSON response returned for software searching' )
@@ -246,3 +298,27 @@ def _is_checksum_matched(f, etag):
246298 for chunk in iter (lambda : f .read (4096 * hash .block_size ), b"" ):
247299 hash .update (chunk )
248300 return hash .hexdigest () == checksum
301+
302+
303+ def _get_valid_filename (software_found ):
304+ """
305+ Ensure that CD Media have correct filenames from description.
306+ Example: S4CORE105_INST_EXPORT_1.zip downloads as 19118000000000004323
307+
308+ Args:
309+ software_found: List[0] with dictionary of file.
310+
311+ Returns:
312+ Valid filename for CD Media files, where applicable.
313+ """
314+
315+ # Check if Title contains filename and extension
316+ if re .match (r'^[^/\\\0]+\.[^/\\\0]+$' , software_found ['Title' ]):
317+ return software_found ['Title' ]
318+ else :
319+ # Check if Description contains filename and extension
320+ if re .match (r'^[^/\\\0]+\.[^/\\\0]+$' , software_found ['Description' ]):
321+ return software_found ['Description' ]
322+ else :
323+ # Default to Title if Description does not help
324+ return software_found ['Title' ]
0 commit comments