Skip to content

Commit 7b6ce3c

Browse files
authored
Merge pull request #485 from AutomationSolutionz/node-enhancements
Implement Automated Cleanup, Python Version Check, and Retry Mechanism for Failed Report Uploads
2 parents 2f49700 + a633fee commit 7b6ce3c

File tree

4 files changed

+131
-32
lines changed

4 files changed

+131
-32
lines changed

Framework/MainDriverApi.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
from rich.console import Console
4646
from rich.box import ASCII_DOUBLE_HEAD, DOUBLE
4747
from rich.padding import Padding
48+
from jinja2 import Environment, FileSystemLoader
49+
from time import sleep
50+
4851
rich_print = Console().print
4952

5053
top_path = os.path.dirname(os.getcwd())
@@ -1533,6 +1536,39 @@ def upload_reports_and_zips(Userid, temp_ini_file, run_id):
15331536
CommonUtil.Exception_Handler(sys.exc_info())
15341537
time.sleep(4)
15351538
else:
1539+
try:
1540+
## Create a folder in failed_upload directory with run_id
1541+
failed_upload_dir = Path(temp_ini_file).parent / 'failed_uploads'
1542+
os.makedirs(failed_upload_dir, exist_ok=True)
1543+
1544+
failed_run_id_dir = failed_upload_dir / run_id
1545+
os.makedirs(failed_run_id_dir, exist_ok=True)
1546+
1547+
## Create a files subfolder files in the run_id folder
1548+
if perf_report_html:
1549+
failed_files_dir = failed_run_id_dir / "files"
1550+
os.makedirs(failed_files_dir, exist_ok=True)
1551+
1552+
## Move the perf_report_html.name to that
1553+
failed_upload_filename = os.path.basename(perf_report_html.name)
1554+
shutil.copy(perf_report_html.name, os.path.join(failed_files_dir,failed_upload_filename))
1555+
else:
1556+
failed_upload_filename = None
1557+
1558+
failed_report_json = {
1559+
"run_id": run_id,
1560+
"method": "POST",
1561+
"URL": "create_report_log_api",
1562+
"execution_report": json.dumps(tc_report),
1563+
"processed_tc_id": processed_tc_id,
1564+
"perf_filepath" : failed_upload_filename
1565+
}
1566+
1567+
failed_report_json_path = failed_run_id_dir / "report.json"
1568+
with open(failed_report_json_path, 'w') as file:
1569+
file.write(json.dumps(failed_report_json))
1570+
except:
1571+
CommonUtil.ExecLog(sModuleInfo, "Could not save the report to retry later of run_id '%s'" % run_id, 3)
15361572
CommonUtil.ExecLog(sModuleInfo, "Could not Upload the report to server of run_id '%s'" % run_id, 3)
15371573

15381574
zip_files = [os.path.join(zip_dir, f) for f in os.listdir(zip_dir) if f.endswith(".zip")]
@@ -1592,6 +1628,48 @@ def upload_reports_and_zips(Userid, temp_ini_file, run_id):
15921628
except:
15931629
CommonUtil.Exception_Handler(sys.exc_info())
15941630

1631+
def retry_failed_report_upload():
1632+
while True:
1633+
try:
1634+
sModuleInfo = inspect.currentframe().f_code.co_name + " : " + MODULE_NAME
1635+
failed_report_dir = PROJECT_ROOT / 'AutomationLog' / 'failed_uploads'
1636+
os.makedirs(failed_report_dir, exist_ok=True)
1637+
folders = [entry.name for entry in failed_report_dir.iterdir() if entry.is_dir()]
1638+
1639+
if folders == []:
1640+
return
1641+
else:
1642+
for folder in folders:
1643+
report_json_path = failed_report_dir / folder / 'report.json'
1644+
report_json = json.load(open(report_json_path))
1645+
if not report_json.get('perf_filepath'):
1646+
res = RequestFormatter.request("post",
1647+
RequestFormatter.form_uri("create_report_log_api/"),
1648+
data={"execution_report": report_json.get('execution_report')},
1649+
verify=False)
1650+
else:
1651+
res = RequestFormatter.request("post",
1652+
RequestFormatter.form_uri("create_report_log_api/"),
1653+
data={"execution_report": report_json.get('execution_report'),
1654+
"processed_tc_id":report_json.get('processed_tc_id')
1655+
1656+
},
1657+
files=[("file",open(failed_report_dir / folder / 'files' /report_json.get('perf_filepath'),'rb'))],
1658+
verify=False)
1659+
1660+
if res.status_code == 200:
1661+
CommonUtil.ExecLog(sModuleInfo, f"Successfully uploaded the execution report of run_id {report_json.get('run_id')}", 1)
1662+
shutil.rmtree(failed_report_dir / folder)
1663+
else:
1664+
CommonUtil.ExecLog(sModuleInfo, f"Unabel to upload the execution report of run_id {report_json.get('run_id')}", 1)
1665+
except Exception as e:
1666+
CommonUtil.ExecLog(sModuleInfo, str(e), 3)
1667+
pass
1668+
1669+
sleep(120)
1670+
1671+
1672+
15951673

15961674
def split_testcases(run_id_info, max_tc_in_single_session):
15971675
import copy

Framework/deploy_handler/long_poll_handler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import random
77
import requests
88
from colorama import Fore
9+
import threading
910

1011
from Framework.Utilities import RequestFormatter
1112

@@ -73,6 +74,7 @@ def run(self, host: str) -> None:
7374

7475
self.on_connect_callback(reconnect)
7576

77+
7678
try:
7779
reconnect = True
7880
resp = RequestFormatter.request("get", host, verify=False)

Framework/module_installer.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,28 @@ def get_req_list():
4444
req_list.append(i.strip())
4545
return req_list
4646

47+
def check_min_python_version(min_python_version,show_warning):
48+
import warnings
49+
version, subversion = list(map(int, min_python_version.split('.')))
50+
# Minimum required version
51+
required_version = (version, subversion)
52+
53+
# Get the current Python version
54+
current_version = sys.version_info[:3]
55+
56+
# Check if the current version is less than the required version
57+
if current_version < required_version:
58+
if not show_warning:
59+
sys.stderr.write(f"Python {required_version[0]}.{required_version[1]} or higher is required.\n")
60+
sys.exit(1)
61+
else:
62+
warning_message = (
63+
f"Warning: You are using Python {current_version[0]}.{current_version[1]}. "
64+
f"Python {required_version[0]}.{required_version[1]} or higher is recommended. Please update your Python version by 28-02-2025."
65+
)
66+
# Show warning in yellow
67+
warnings.warn(f"\033[93m{warning_message}\033[0m")
68+
4769
def install_missing_modules(req_list=None):
4870
"""
4971
Purpose: This function will check all the installed modules, compare with what is in requirements-win.txt file

node_cli.py

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from datetime import datetime as dt
1414

1515
import time
16-
16+
import threading
1717

1818
# Disable WebdriverManager SSL verification.
1919
os.environ['WDM_SSL_VERIFY'] = '0'
@@ -27,7 +27,9 @@
2727
print(version_path.read_text().strip())
2828
print("[Python version]")
2929
print("Python " + platform.python_version() + "(" + platform.architecture()[0] + ")\n")
30-
from Framework.module_installer import install_missing_modules,update_outdated_modules
30+
from Framework.module_installer import check_min_python_version, install_missing_modules,update_outdated_modules
31+
32+
check_min_python_version(min_python_version="3.11",show_warning=True)
3133
install_missing_modules()
3234

3335
# Conditionally monkey-patch datetime module to include the `fromisoformat` method.
@@ -389,6 +391,10 @@ def Login(cli=False, run_once=False, log_dir=None):
389391
}
390392
)
391393
node_id = CommonUtil.MachineInfo().getLocalUser().lower()
394+
from Framework.MainDriverApi import retry_failed_report_upload
395+
report_thread = threading.Thread(target=retry_failed_report_upload, daemon=True)
396+
report_thread.start()
397+
392398
RunProcess(node_id, run_once=run_once, log_dir=log_dir)
393399

394400
if run_once:
@@ -926,39 +932,30 @@ def get_subfolders_created_before_n_days(folder_path, log_delete_interval):
926932

927933
folder_path = os.path.dirname(os.path.abspath(__file__)).replace(os.sep + "Framework", os.sep + '') + os.sep + 'AutomationLog'
928934
log_delete_interval = ConfigModule.get_config_value("Advanced Options", "log_delete_interval")
929-
if log_delete_interval:
930-
auto_log_subfolders = get_subfolders_created_before_n_days(folder_path,int(log_delete_interval))
931-
auto_log_subfolders = [subfolder for subfolder in auto_log_subfolders if subfolder not in ['attachments','attachments_db','outdated_modules.json','temp_config.ini']]
932935

933-
if auto_log_subfolders:
936+
# By default set the automation log delete interval to 7 days
937+
if not isinstance(log_delete_interval,int):
938+
log_delete_interval = 7
939+
else:
940+
if log_delete_interval <= 0:
941+
log_delete_interval = 7
942+
943+
def delete_old_automationlog_folders():
944+
while True:
945+
auto_log_subfolders = get_subfolders_created_before_n_days(folder_path,int(log_delete_interval))
946+
auto_log_subfolders = [subfolder for subfolder in auto_log_subfolders if subfolder not in ['attachments','attachments_db','outdated_modules.json','temp_config.ini','failed_reports']]
947+
934948
for subfolder in auto_log_subfolders:
935949
shutil.rmtree(subfolder)
936-
print(f'automation_log_cleanup: deleted {len(auto_log_subfolders)} that are older than {log_delete_interval} days')
937-
938-
folder_path = os.path.dirname(os.path.abspath(__file__)).replace(os.sep + "Framework",
939-
os.sep + '') + os.sep + 'AutomationLog'
940-
log_date_str = config.get('Advanced Options', {}).get('last_log_delete_date', '')
941-
log_delete_interval = config.get('Advanced Options', {}).get('log_delete_interval', '')
942-
if log_date_str:
943-
log_config_date = date.fromisoformat(log_date_str)
944-
current_date = datetime.date.today()
945-
time_difference = (current_date - log_config_date).days
946-
if time_difference > int(log_delete_interval):
947-
print("Cleaning Up AutomationLog Folder...")
948-
for root, dirs, files in os.walk(folder_path, topdown=False):
949-
for dir_name in dirs:
950-
folder = os.path.join(root, dir_name)
951-
shutil.rmtree(folder)
952-
config.setdefault('Advanced Options', {})['last_log_delete_date'] = str(date.today())
953-
config.write()
954-
else:
955-
pass
956-
# remaining_time = 7 - time_difference
957-
# print(f"AutomationLog Folder will be deleted after {remaining_time+1} Days")
958-
else:
959-
config.setdefault('Advanced Options', {})['last_log_delete_date'] = str(date.today())
960-
config.write()
961-
# print("AutomationLog Folder Not Found")
950+
if auto_log_subfolders:
951+
print(f'automation_log_cleanup: deleted {len(auto_log_subfolders)} that are older than {log_delete_interval} days')
952+
953+
# Check every 5 hours for old automation logs
954+
time.sleep(60*60*5)
955+
956+
# Create a background thread for deleting automation log
957+
thread = threading.Thread(target=delete_old_automationlog_folders, daemon=True)
958+
thread.start()
962959

963960
if show_browser_log:
964961
CommonUtil.show_browser_log = True

0 commit comments

Comments
 (0)