views.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. from flask import render_template, redirect, flash, request, url_for, send_from_directory
  2. from flask_login import login_required, current_user, login_user, logout_user
  3. from flask.logging import default_handler
  4. from app import app, db, models, forms, utils
  5. from datetime import datetime
  6. # handle favicon requests
  7. @app.route('/favicon.ico')
  8. def favicon():
  9. return send_from_directory('static/media', 'favicon.ico', mimetype='image/vnd.microsoft.icon')
  10. @app.route('/')
  11. @login_required
  12. def index():
  13. return render_template('testrun/index.html', items=utils.getItemCategories())
  14. @app.route('/<string:item_type>')
  15. @login_required
  16. def item_list(item_type):
  17. # placeholder for import form
  18. form = None
  19. # get item list by type
  20. if item_type == 'testrun':
  21. items = models.Testrun.query.all()
  22. # build form for importing a testrun
  23. form = forms.TestrunImportForm()
  24. elif item_type == 'testcase_sequence':
  25. items = models.TestCaseSequence.query.all()
  26. elif item_type == 'testcase':
  27. items = models.TestCase.query.all()
  28. elif item_type == 'teststep_sequence':
  29. items = models.TestStepSequence.query.all()
  30. elif item_type == 'teststep':
  31. items = models.TestStepExecution.query.all()
  32. else:
  33. app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
  34. flash(f'Item type "{item_type}" does not exist.', 'warning')
  35. return redirect(url_for('index'))
  36. return render_template('testrun/item_list.html', type=item_type, items=items, form=form)
  37. @app.route('/<string:item_type>/<int:item_id>', methods=['GET', 'POST'])
  38. @login_required
  39. def get_item(item_type, item_id):
  40. # get item by type and id
  41. if item_type == 'testrun':
  42. item = models.Testrun.query.get(item_id)
  43. elif item_type == 'testcase_sequence':
  44. item = models.TestCaseSequence.query.get(item_id)
  45. elif item_type == 'testcase':
  46. item = models.TestCase.query.get(item_id)
  47. elif item_type == 'teststep_sequence':
  48. item = models.TestStepSequence.query.get(item_id)
  49. elif item_type == 'teststep':
  50. item = models.TestStepExecution.query.get(item_id)
  51. else:
  52. app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
  53. flash(f'Item type "{item_type}" does not exist.', 'warning')
  54. return redirect(url_for('index'))
  55. return render_template('testrun/item.html', type=item_type, item=item)
  56. @app.route('/<string:item_type>/<int:item_id>/delete', methods=['POST', 'DELETE'])
  57. @login_required
  58. def delete_item(item_type, item_id):
  59. #
  60. # delete item
  61. #
  62. # cascade delete
  63. if request.method == 'POST':
  64. try:
  65. utils.deleteCascade(item_type, item_id)
  66. flash(f'Item {utils.getItemType(item_type)} ID #{item_id} and its children have been successfully deleted.', 'success')
  67. return 'Success', 200
  68. except Exception as error:
  69. flash(error, 'warning')
  70. return 'Bad Request', 400
  71. # delete single item
  72. elif request.method == 'DELETE':
  73. # get item by type and id
  74. if item_type == 'testrun':
  75. item = models.Testrun.query.get(item_id)
  76. elif item_type == 'testcase_sequence':
  77. item = models.TestCaseSequence.query.get(item_id)
  78. elif item_type == 'testcase':
  79. item = models.TestCase.query.get(item_id)
  80. elif item_type == 'teststep_sequence':
  81. item = models.TestStepSequence.query.get(item_id)
  82. elif item_type == 'teststep':
  83. item = models.TestStepExecution.query.get(item_id)
  84. else:
  85. app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
  86. flash(f'Item type "{item_type}" does not exist.', 'warning')
  87. #return redirect(url_for('index'))
  88. return 'Bad Request', 400
  89. db.session.delete(item)
  90. db.session.commit()
  91. app.logger.info(f'Deleted {item_type} id {item_id} by {current_user}.')
  92. flash(f'Item "{item.name}" has been successfully deleted.', 'success')
  93. #return redirect(url_for('item_list', item_type=item_type))
  94. return 'Success', 200
  95. return 'Method Not Allowed', 405
  96. @app.route('/<string:item_type>/<int:item_id>/edit', methods=['GET', 'POST'])
  97. @login_required
  98. def edit_item(item_type, item_id):
  99. #
  100. # edit item
  101. #
  102. if item_type == 'testrun':
  103. item = models.Testrun.query.get(item_id)
  104. form = forms.TestrunCreateForm.new()
  105. if request.method == 'GET':
  106. form.testcase_sequences.data = [f'{x.id}' for x in item.testcase_sequences]
  107. elif item_type == 'testcase_sequence':
  108. item = models.TestCaseSequence.query.get(item_id)
  109. form = forms.TestCaseSequenceCreateForm.new()
  110. if request.method == 'GET':
  111. form.classname.data = f'{item.classname.id}'
  112. form.datafiles.data = [f'{x.id}' for x in item.datafiles]
  113. form.testcases.data = [f'{x.id}' for x in item.testcases]
  114. elif item_type == 'testcase':
  115. item = models.TestCase.query.get(item_id)
  116. form = forms.TestCaseCreateForm.new()
  117. if request.method == 'GET':
  118. form.classname.data = f'{item.classname.id}'
  119. form.browser_type.data = f'{item.browser_type.id}'
  120. form.testcase_type.data = f'{item.testcase_type.id}'
  121. form.testcase_stepsequences.data = [f'{x.id}' for x in item.teststep_sequences]
  122. elif item_type == 'teststep_sequence':
  123. item = models.TestStepSequence.query.get(item_id)
  124. form = forms.TestStepSequenceCreateForm.new()
  125. if request.method == 'GET':
  126. form.classname.data = f'{item.classname.id}'
  127. form.teststeps.data =[f'{x.id}' for x in item.teststeps]
  128. elif item_type == 'teststep':
  129. item = models.TestStepExecution.query.get(item_id)
  130. form = forms.TestStepCreateForm.new()
  131. if request.method == 'GET':
  132. form.activity_type.data = f'{item.activity_type.id}'
  133. form.locator_type.data = f'{item.locator_type.id}'
  134. # model extension
  135. form.locator.data = item.locator or ''
  136. if item.optional:
  137. form.optional.data = '2'
  138. else:
  139. form.optional.data = '1'
  140. if item.timeout:
  141. form.timeout.data = f'{item.timeout}'
  142. else:
  143. form.timeout.data = ''
  144. form.release.data = item.release or ''
  145. form.value.data = item.value or ''
  146. form.value2.data = item.value2 or ''
  147. form.comparision.data = utils.getComparisionId(item.comparision)
  148. else:
  149. app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
  150. flash(f'Item type "{item_type}" does not exist.', 'warning')
  151. return redirect(url_for('index'))
  152. if request.method == 'GET':
  153. form.name.data = item.name
  154. form.description.data = item.description
  155. if form.validate_on_submit():
  156. # update item data
  157. item.name = form.name.data
  158. item.description = form.description.data
  159. # testrun
  160. if item_type == 'testrun':
  161. item.editor = current_user
  162. item.edited = datetime.utcnow()
  163. item.testcase_sequences=[models.TestCaseSequence.query.get(int(x)) for x in form.testcase_sequences.data]
  164. # testcase sequence
  165. elif item_type == 'testcase_sequence':
  166. item.editor = current_user
  167. item.edited = datetime.utcnow()
  168. item.classname = models.ClassName.query.get(int(form.classname.data))
  169. item.datafiles = [models.DataFile.query.get(int(x)) for x in form.datafiles.data]
  170. item.testcases = [models.TestCase.query.get(int(x)) for x in form.testcases.data]
  171. # testcase
  172. elif item_type == 'testcase':
  173. item.editor = current_user
  174. item.edited = datetime.utcnow()
  175. item.classname = models.ClassName.query.get(int(form.classname.data))
  176. item.browser_type = models.BrowserType.query.get(int(form.browser_type.data))
  177. item.testcase_type = models.TestCaseType.query.get(int(form.testcase_type.data))
  178. item.teststep_sequences = [models.TestStepSequence.query.get(int(x)) for x in form.testcase_stepsequences.data]
  179. # step sequence
  180. elif item_type == 'teststep_sequence':
  181. item.editor = current_user
  182. item.edited = datetime.utcnow()
  183. item.classname = models.ClassName.query.get(int(form.classname.data))
  184. item.teststeps = [models.TestStepExecution.query.get(int(x)) for x in form.teststeps.data]
  185. # test step
  186. elif item_type == 'teststep':
  187. item.editor = current_user
  188. item.edited = datetime.utcnow()
  189. item.activity_type = models.ActivityType.query.get(int(form.activity_type.data))
  190. item.locator_type = models.LocatorType.query.get(int(form.locator_type.data))
  191. # model extension
  192. item.locator = form.locator.data
  193. item.optional = [None, False, True][int(form.optional.data)]
  194. try:
  195. item.timeout = float(form.timeout.data)
  196. except ValueError:
  197. item.timeout = None
  198. item.release = form.release.data or None
  199. item.value = form.value.data or None
  200. item.value2 = form.value2.data or None
  201. if form.comparision.data == '0':
  202. item.comparision = None
  203. else:
  204. item.comparision = utils.COMPARISIONS[int(form.comparision.data)-1]
  205. # update item in db
  206. db.session.commit()
  207. app.logger.info(f'Edited {item_type} id {item_id} by {current_user}.')
  208. flash(f'Item "{item.name}" successfully updated.', 'success')
  209. return redirect(url_for('item_list', item_type=item_type))
  210. return render_template('testrun/edit_item.html', type=item_type, item=item, form=form)
  211. @app.route('/<string:item_type>/new', methods=['GET', 'POST'])
  212. @login_required
  213. def new_item(item_type):
  214. #
  215. # create new item
  216. #
  217. if item_type == 'testrun':
  218. form = forms.TestrunCreateForm.new()
  219. chips = ['testcase_sequences']
  220. elif item_type == 'testcase_sequence':
  221. form = forms.TestCaseSequenceCreateForm.new()
  222. chips = ['datafiles', 'testcases']
  223. elif item_type == 'testcase':
  224. form = forms.TestCaseCreateForm.new()
  225. chips = ['testcase_stepsequences']
  226. elif item_type == 'teststep_sequence':
  227. form = forms.TestStepSequenceCreateForm.new()
  228. chips = ['teststeps']
  229. elif item_type == 'teststep':
  230. form = forms.TestStepCreateForm.new()
  231. chips = []
  232. else:
  233. app.logger.warning(f'Item type "{item_type}" does not exist. Requested by "{current_user}".')
  234. flash(f'Item type "{item_type}" does not exist.', 'warning')
  235. return redirect(url_for('index'))
  236. if form.validate_on_submit():
  237. # create new item
  238. # testrun
  239. if item_type == 'testrun':
  240. item = models.Testrun(
  241. name=form.name.data,
  242. description=form.description.data,
  243. creator=current_user,
  244. testcase_sequences=[models.TestCaseSequence.query.get(int(x)) for x in form.testcase_sequences.data],
  245. )
  246. # testcase sequence
  247. elif item_type == 'testcase_sequence':
  248. item = models.TestCaseSequence(
  249. name=form.name.data,
  250. description=form.description.data,
  251. creator=current_user,
  252. classname=models.ClassName.query.get(int(form.classname.data)),
  253. datafiles=[models.DataFile.query.get(int(x)) for x in form.datafiles.data],
  254. testcases=[models.TestCase.query.get(int(x)) for x in form.testcases.data],
  255. )
  256. # testcase
  257. elif item_type == 'testcase':
  258. item = models.TestCase(
  259. name=form.name.data,
  260. description=form.description.data,
  261. creator=current_user,
  262. classname=models.ClassName.query.get(int(form.classname.data)),
  263. browser_type=models.BrowserType.query.get(int(form.browser_type.data)),
  264. testcase_type=models.TestCaseType.query.get(int(form.testcase_type.data)),
  265. teststep_sequences=[models.TestStepSequence.query.get(int(x)) for x in form.testcase_stepsequences.data],
  266. )
  267. # step sequence
  268. elif item_type == 'teststep_sequence':
  269. item = models.TestStepSequence(
  270. name=form.name.data,
  271. description=form.description.data,
  272. creator=current_user,
  273. classname=models.ClassName.query.get(int(form.classname.data)),
  274. teststeps=[models.TestStepExecution.query.get(int(x)) for x in form.teststeps.data],
  275. )
  276. # test step
  277. elif item_type == 'teststep':
  278. item = models.TestStepExecution(
  279. name=form.name.data,
  280. description=form.description.data,
  281. creator=current_user,
  282. activity_type=models.ActivityType.query.get(int(form.activity_type.data)),
  283. locator_type=models.LocatorType.query.get(int(form.locator_type.data)),
  284. # model extension
  285. locator=form.locator.data,
  286. optional=[None, False, True][int(form.optional.data)],
  287. )
  288. try:
  289. item.timeout = float(form.timeout.data)
  290. except ValueError:
  291. item.timeout = None
  292. item.release = form.release.data or None
  293. item.value = form.value.data or None
  294. item.value2 = form.value2.data or None
  295. if form.comparision.data == '0':
  296. item.comparision = None
  297. else:
  298. item.comparision = utils.COMPARISIONS[int(form.comparision.data)-1]
  299. # save item to db
  300. db.session.add(item)
  301. db.session.commit()
  302. app.logger.info(f'Created {item_type} id {item.id} by {current_user}.')
  303. flash(f'Item "{item.name}" successfully created.', 'success')
  304. return redirect(url_for('item_list', item_type=item_type))
  305. return render_template('testrun/create_item.html', type=item_type, chips=chips, form=form)
  306. #return render_template('testrun/edit_item.html', type=item_type, item=None, form=form)
  307. @app.route('/testrun/xlsx/<int:item_id>', methods=['GET', 'POST'])
  308. @login_required
  309. def to_xlsx(item_id):
  310. #
  311. # export Testrun object to XLSX
  312. #
  313. result = utils.exportXLSX(item_id)
  314. url = url_for('static', filename=f'files/{result}')
  315. #flash(f'Testrun #{item_id} successfully exported to XLSX.', 'success')
  316. #return redirect(url_for('item_list', item_type='testrun'))
  317. app.logger.info(f'Imported Testrun id {item_id} by {current_user}. Target: static/{filename}')
  318. return f'Success: <a href="{url}">{result}</a>'
  319. @app.route('/testrun/import', methods=['POST'])
  320. @login_required
  321. def import_testsun():
  322. #
  323. # imports testrun from file
  324. #
  325. # import only from XLSX is available now
  326. form = forms.TestrunImportForm()
  327. if form.validate_on_submit():
  328. #utils.importXLSX(form.file.data)
  329. try:
  330. utils.importXLSX(form.file.data)
  331. app.logger.info(f'Testrun successfully imported from "{form.file.data.filename}" by {current_user}.')
  332. flash(f'Testrun successfully imported from "{form.file.data.filename}"', 'success')
  333. except Exception as error:
  334. app.logger.error(f'Failed to import Testrun from "{form.file.data.filename}" by {current_user}. {error}.')
  335. flash(f'ERROR: Cannot import Testrun from "{form.file.data.filename}". {error}.', 'danger')
  336. else:
  337. flash(f'File is required for import', 'warning')
  338. return redirect(url_for('item_list', item_type='testrun'))
  339. @app.route('/testrun/<int:item_id>/import', methods=['POST'])
  340. @login_required
  341. def update_testsun(item_id):
  342. #
  343. # imports testrun from file
  344. #
  345. # update only from XLSX is available now
  346. form = forms.TestrunImportForm()
  347. if form.validate_on_submit():
  348. print('******** FORM:')
  349. for field in form:
  350. print(f'{field.name}:\t{field.data}')
  351. # update items
  352. #utils.importXLSX(form.file.data, item_id=item_id)
  353. try:
  354. utils.importXLSX(form.file.data, item_id=item_id)
  355. app.logger.info(f'Testrun ID #{item_id} successfully updated from "{form.file.data.filename}" by {current_user}.')
  356. flash(f'Testrun ID #{item_id} has been successfully updated from "{form.file.data.filename}"', 'success')
  357. except Exception as error:
  358. app.logger.error(f'Failed to update Testrun ID #{item_id} from "{form.file.data.filename}" by {current_user}. {error}.')
  359. flash(f'ERROR: Cannot update Testrun ID #{item_id} from "{form.file.data.filename}". {error}.', 'danger')
  360. else:
  361. flash(f'File is required for import', 'warning')
  362. return redirect(url_for('item_list', item_type='testrun'))
  363. #
  364. # user authentication
  365. #
  366. @app.route('/signup', methods=['GET', 'POST'])
  367. def signup():
  368. if current_user.is_authenticated:
  369. return redirect(url_for('index'))
  370. form = forms.SingupForm()
  371. if form.validate_on_submit():
  372. # create user
  373. user = models.User(username=form.username.data)
  374. user.set_password(form.password.data)
  375. db.session.add(user)
  376. db.session.commit()
  377. # login
  378. login_user(user, remember=True)
  379. flash(f'User {user.username.capitalize()} successfully created!', 'succsess')
  380. return redirect(url_for('index'))
  381. return render_template('testrun/signup.html', form=form)
  382. @app.route('/login', methods=['GET', 'POST'])
  383. def login():
  384. if current_user.is_authenticated:
  385. return redirect(url_for('index'))
  386. form = forms.LoginForm()
  387. if form.validate_on_submit():
  388. user = models.User.query.filter_by(username=form.username.data).first()
  389. if user and user.verify_password(form.password.data):
  390. login_user(user, remember=True)
  391. flash(f'Welcome {user.username.capitalize()}!', 'success')
  392. return redirect(url_for('index'))
  393. return render_template('testrun/login.html', form=form)
  394. @app.route('/logout')
  395. def logout():
  396. logout_user()
  397. return redirect(url_for('login'))