HandleDatabase.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. import logging
  2. from xlrd3 import open_workbook
  3. import json
  4. import baangt.base.CustGlobalConstants as CGC
  5. import baangt.base.GlobalConstants as GC
  6. from baangt.base.Utils import utils
  7. import re
  8. from random import randint
  9. from openpyxl import load_workbook
  10. from baangt.TestDataGenerator.TestDataGenerator import TestDataGenerator
  11. logger = logging.getLogger("pyC")
  12. class Writer:
  13. """
  14. This class is made to update existing excel file.
  15. First it will open the file in python and then we can do multiple writes and once everything is update we can use
  16. save method in order to save the updated excel file. Hence, this class is very useful is saving time while updating
  17. excel files.
  18. """
  19. def __init__(self, path):
  20. self.path = path
  21. self.workbook = load_workbook(self.path)
  22. def write(self, row, data, sht):
  23. # Update the values using row and col number.
  24. # Note :- We are using openpyxl so row & column index will start from 1 instead of 0
  25. column = 0
  26. sheet = self.workbook[sht]
  27. headers = next(sheet.rows)
  28. for header in headers: # finds the header position
  29. if "usecount" in str(header.value).lower():
  30. column = headers.index(header) + 1
  31. if column:
  32. sheet.cell(row, column).value = data
  33. def save(self):
  34. # Call this method to save the file once every updates are written
  35. self.workbook.save(self.path)
  36. self.workbook.close()
  37. class HandleDatabase:
  38. def __init__(self, linesToRead, globalSettings=None):
  39. self.lineNumber = 3
  40. # FIXME: This is still not clean. GlobalSettings shouldn't be predefined in CustomConstants-Class
  41. self.globals = {
  42. CGC.CUST_TOASTS: "",
  43. GC.EXECUTION_STAGE: "",
  44. GC.TESTCASEERRORLOG: "",
  45. CGC.VIGOGFNUMMER: "",
  46. CGC.SAPPOLNR: "",
  47. CGC.PRAEMIE: "",
  48. CGC.POLNRHOST: "",
  49. GC.TESTCASESTATUS: "",
  50. GC.TIMING_DURATION: "",
  51. GC.SCREENSHOTS: "",
  52. GC.TIMELOG: "",
  53. }
  54. if globalSettings:
  55. for setting, value in globalSettings.items():
  56. self.globals[setting] = value
  57. self.range = self.__buildRangeOfRecords(linesToRead)
  58. self.rangeDict = {}
  59. self.__buildRangeDict()
  60. self.df_json = None
  61. self.dataDict = []
  62. self.recordPointer = 0
  63. self.sheet_dict = {}
  64. self.usecount = False
  65. def __buildRangeDict(self):
  66. """
  67. Interprets the Range and creates a DICT of values, that we can loop over later
  68. @return: Creates empty self.rangeDict
  69. """
  70. for lRangeLine in self.range:
  71. for x in range(lRangeLine[0], lRangeLine[1]+1):
  72. self.rangeDict[x] = ""
  73. def __buildRangeOfRecords(self, rangeFromConfigFile):
  74. lRange = []
  75. if not rangeFromConfigFile:
  76. # No selection - means all records
  77. return [[0,99999]]
  78. else:
  79. # Format: 4;6-99;17-200;203 or
  80. # Format: 4,6-100,800-1000
  81. if ";" in rangeFromConfigFile:
  82. for kombination in rangeFromConfigFile.split(";"):
  83. lRange.append(HandleDatabase.__buildRangeOfRecordsOneEntry(kombination))
  84. elif "," in rangeFromConfigFile:
  85. for kombination in rangeFromConfigFile.split(","):
  86. lRange.append(HandleDatabase.__buildRangeOfRecordsOneEntry(kombination))
  87. else:
  88. lRange.append(HandleDatabase.__buildRangeOfRecordsOneEntry(rangeFromConfigFile))
  89. # Make sure these are numbers:
  90. for lRangeLine in lRange:
  91. lRangeLine[0] = HandleDatabase.__sanitizeNumbers(lRangeLine[0])
  92. lRangeLine[1] = HandleDatabase.__sanitizeNumbers(lRangeLine[1])
  93. return lRange
  94. @staticmethod
  95. def __sanitizeNumbers(numberIn):
  96. if isinstance(numberIn, dict):
  97. numberIn = numberIn['default']
  98. try:
  99. return int(numberIn.strip())
  100. except:
  101. return 0
  102. numberIn = numberIn.strip()
  103. return int(numberIn)
  104. @staticmethod
  105. def __buildRangeOfRecordsOneEntry(rangeIn):
  106. if "-" in rangeIn:
  107. # This is a range (17-22)
  108. return HandleDatabase.__buildRangeOfRecordsSingleRange(rangeIn)
  109. else:
  110. # This is a single entry:
  111. return [rangeIn, rangeIn]
  112. @staticmethod
  113. def __buildRangeOfRecordsSingleRange(rangeIn):
  114. lSplit = rangeIn.split("-")
  115. return [lSplit[0], lSplit[1]]
  116. def read_excel(self, fileName, sheetName):
  117. fileName = utils.findFileAndPathFromPath(fileName)
  118. if not fileName:
  119. logger.critical(f"Can't open file: {fileName}")
  120. return
  121. logger.debug(f"Reading excel file {fileName}...")
  122. book = open_workbook(fileName)
  123. sheet = book.sheet_by_name(sheetName)
  124. # read header values into the list
  125. keys = [sheet.cell(0, col_index).value for col_index in range(sheet.ncols)]
  126. # if testresult header is present then taking its index, which is later used as column number
  127. testrun_index = [keys.index(x) for x in keys if str(x).lower() == "testresult"]
  128. if testrun_index:
  129. testrun_index = testrun_index[0] + 1 # adding +1 value which is the correct column position
  130. else: # if list is empty that means their is no testresult header
  131. testrun_index = 0
  132. for row_index in range(1, sheet.nrows):
  133. temp_dic = {}
  134. for col_index in range(sheet.ncols):
  135. temp_dic[keys[col_index]] = sheet.cell(row_index, col_index).value
  136. if type(temp_dic[keys[col_index]]) == float:
  137. temp_dic[keys[col_index]] = repr(temp_dic[keys[col_index]])
  138. if temp_dic[keys[col_index]][-2:] == ".0":
  139. temp_dic[keys[col_index]] = temp_dic[keys[col_index]][:-2]
  140. # row, column, sheetName & fileName which are later used in updating source testrun file
  141. temp_dic["testcase_row"] = row_index
  142. temp_dic["testcase_sheet"] = sheetName
  143. temp_dic["testcase_file"] = fileName
  144. temp_dic["testcase_column"] = testrun_index
  145. self.dataDict.append(temp_dic)
  146. def update_datarecords(self, dataDict, fileName, sheetName, noCloneXls):
  147. logger.debug("Updating prefix data...")
  148. self.testDataGenerator = TestDataGenerator(fileName, sheetName=sheetName,
  149. from_handleDatabase=True, noUpdate=noCloneXls)
  150. for td in dataDict:
  151. temp_dic = dataDict[td]
  152. new_data_dic = {}
  153. for keys in temp_dic:
  154. if type(temp_dic[keys]) != str:
  155. continue
  156. if '$(' in str(temp_dic[keys]):
  157. while '$(' in str(temp_dic[keys]):
  158. start_index = temp_dic[keys].index('$(')
  159. end_index = temp_dic[keys][start_index:].index(')')+start_index
  160. data_to_replace_with = temp_dic[temp_dic[keys][start_index+2:end_index]]
  161. temp_dic[keys] = temp_dic[keys].replace(
  162. temp_dic[keys][start_index:end_index+1], data_to_replace_with
  163. )
  164. if str(temp_dic[keys])[:4].upper() == "RRD_":
  165. logger.debug(f"Processing rrd data - {temp_dic[keys]}")
  166. rrd_data = self.get_data_from_tdg(temp_dic[keys])
  167. self.testDataGenerator.usecount_dict[repr(rrd_data)]["use"] += 1
  168. self.testDataGenerator.update_usecount_in_source(rrd_data)
  169. for data in rrd_data:
  170. new_data_dic[data] = rrd_data[data]
  171. logger.debug(f"Data processed - {temp_dic[keys]}")
  172. elif str(temp_dic[keys])[:4].upper() == "RRE_":
  173. logger.debug(f"Processing rre data - {temp_dic[keys]}")
  174. rre_data = self.get_data_from_tdg(temp_dic[keys])
  175. self.testDataGenerator.usecount_dict[repr(rre_data)]["use"] += 1
  176. self.testDataGenerator.update_usecount_in_source(rre_data)
  177. for data in rre_data:
  178. new_data_dic[data] = rre_data[data]
  179. logger.debug(f"Data processed - {temp_dic[keys]}")
  180. elif str(temp_dic[keys])[:4].upper() == "RLP_":
  181. temp_dic[keys] = self.rlp_process(temp_dic[keys], fileName)
  182. elif str(temp_dic[keys])[:5].upper() == "RENV_":
  183. temp_dic[keys] = str(TestDataGenerator.get_env_variable(temp_dic[keys][5:]))
  184. else:
  185. try:
  186. js = json.loads(temp_dic[keys])
  187. temp_dic[keys] = js
  188. except:
  189. pass
  190. for key in new_data_dic:
  191. temp_dic[key] = new_data_dic[key]
  192. self.testDataGenerator.save_usecount()
  193. def get_data_from_tdg(self, string):
  194. data = self.testDataGenerator.data_generators(string)
  195. if self.testDataGenerator.usecount_dict[repr(data[0])]["limit"]:
  196. data = [d for d in data if self.testDataGenerator.usecount_dict[repr(d)]["use"
  197. ] < self.testDataGenerator.usecount_dict[repr(d)]["limit"]]
  198. if len(data) > 1:
  199. data = data[randint(0, len(data) - 1)]
  200. elif len(data) == 1:
  201. data = data[0]
  202. else:
  203. raise BaseException(f"Not enough data for {string}, please verify if data is present or usecount limit" \
  204. "has reached!!")
  205. return data
  206. def rlp_process(self, string, fileName):
  207. # Will get real data from rlp_ prefix string
  208. rlp_string = self.__process_rlp_string(string)[5:-1]
  209. rlp_data = self.__rlp_string_to_python(rlp_string, fileName)
  210. data = rlp_data
  211. self.rlp_iterate(data, fileName)
  212. return data
  213. def rlp_iterate(self, data, fileName):
  214. # Rlp datas are stored in either json or list. This function will loop on every data and convert every
  215. # Rlp string to data
  216. if type(data) is list:
  217. for dt in data:
  218. if type(dt) is str:
  219. dt = self.rlp_iterate(dt, fileName)
  220. elif type(dt) is dict or type(dt) is list:
  221. dt = self.rlp_iterate(dt, fileName)
  222. elif type(data) is dict:
  223. for key in data:
  224. if type(data[key]) is list or type(data[key]) is dict:
  225. data[key] = self.rlp_iterate(data[key], fileName)
  226. elif type(data[key]) is str:
  227. data[key] = self.rlp_iterate(data[key], fileName)
  228. elif type(data) is str:
  229. if data[:4] == "RLP_":
  230. data = self.rlp_process(data, fileName)
  231. return data
  232. def __rlp_string_to_python(self, raw_data, fileName):
  233. # will convert rlp string to python
  234. sheetName = raw_data.split(',')[0].strip()
  235. headerName = raw_data.split(',')[1].strip().split('=')[0].strip()
  236. headerValue = raw_data.split(',')[1].strip().split('=')[1].strip()
  237. all_sheets, main_sheet = self.testDataGenerator.read_excel(path=fileName, sheet_name=sheetName, return_json=True)
  238. data_list = []
  239. for data in main_sheet:
  240. main_value = data[headerName]
  241. if type(main_value) == float and str(main_value)[-2:] == '.0':
  242. main_value = str(int(main_value))
  243. if main_value.strip() == headerValue:
  244. for key in data:
  245. try:
  246. js = json.loads(data[key])
  247. data[key] = js
  248. except:
  249. pass
  250. data_list.append(data)
  251. return data_list
  252. def __process_rlp_string(self, rlp_string):
  253. processed_string = ','.join([word.strip() for word in rlp_string.split(', ')])
  254. match = re.match(
  255. r"(RLP_(\(|\[)).+,.+=.+(\]|\))",
  256. processed_string
  257. )
  258. err_string = f"{rlp_string} not matching pattern RLP_(sheetName,HeaderName=DataToMatch"
  259. assert match, err_string
  260. return processed_string
  261. def __compareEqualStageInGlobalsAndDataRecord(self, currentNewRecordDict:dict) -> bool:
  262. """
  263. As method name says, compares, whether Stage in Global-settings is equal to stage in Data Record,
  264. so that this record might be excluded, if it's for the wrong Stage.
  265. :param currentNewRecordDict: The current Record
  266. :return: Boolean
  267. """
  268. lAppend = True
  269. if self.globals.get(GC.EXECUTION_STAGE):
  270. if currentNewRecordDict.get(GC.EXECUTION_STAGE):
  271. if currentNewRecordDict[GC.EXECUTION_STAGE] != self.globals[GC.EXECUTION_STAGE]:
  272. lAppend = False
  273. return lAppend
  274. def readNextRecord(self):
  275. """
  276. We built self.range during init. Now we need to iterate over the range(s) in range,
  277. find appropriate record and return that - one at a time
  278. @return:
  279. """
  280. if len(self.rangeDict) == 0:
  281. # All records were processed
  282. return None
  283. try:
  284. # the topmost record of the RangeDict (RangeDict was built by the range(s) from the TestRun
  285. # - 1 because there's a header line in the Excel-Sheet.
  286. lRecord = self.dataDict[(list(self.rangeDict.keys())[0])]
  287. while not self.__compareEqualStageInGlobalsAndDataRecord(lRecord):
  288. logger.debug(f"Skipped record {str(lRecord)[:30]} due to wrong stage: {lRecord[GC.EXECUTION_STAGE]} vs. "
  289. f"{self.globals[GC.EXECUTION_STAGE]}")
  290. self.rangeDict.pop(list(self.rangeDict.keys())[0])
  291. lRecord = self.dataDict[(list(self.rangeDict.keys())[0])]
  292. except Exception as e:
  293. logger.debug(f"Couldn't read record from database: {list(self.rangeDict.keys())[0]}")
  294. self.rangeDict.pop(list(self.rangeDict.keys())[0])
  295. return None
  296. # Remove the topmost entry from the rangeDict, so that next time we read the next entry in the lines above
  297. self.rangeDict.pop(list(self.rangeDict.keys())[0])
  298. return self.updateGlobals(lRecord)
  299. def readTestRecord(self, lineNumber=None):
  300. if lineNumber:
  301. self.lineNumber = lineNumber -1 # Base 0 vs. Base 1
  302. else:
  303. self.lineNumber += 1 # add 1 to read next line number
  304. try:
  305. record = self.dataDict[self.lineNumber]
  306. logger.info(f"Starting with Testrecord {self.lineNumber}, Details: " +
  307. str({k: record[k] for k in list(record)[0:5]}))
  308. return self.updateGlobals(record)
  309. except Exception as e:
  310. logger.critical(f"Couldn't read record# {self.lineNumber}")
  311. def updateGlobals(self, record):
  312. self.globals[CGC.CUST_TOASTS] = ""
  313. self.globals[GC.TESTCASEERRORLOG] = ""
  314. self.globals[GC.TIMING_DURATION] = ""
  315. self.globals[GC.TIMELOG] = ""
  316. self.globals[GC.TESTCASESTATUS] = ""
  317. self.globals[GC.SCREENSHOTS] = ""
  318. record.update(self.globals)
  319. return record