init
This commit is contained in:
81
app/routes/admin.py
Normal file
81
app/routes/admin.py
Normal file
@@ -0,0 +1,81 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
from app.models import User
|
||||
from app import db
|
||||
from functools import wraps
|
||||
|
||||
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
|
||||
|
||||
def admin_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if not current_user.is_authenticated or not current_user.is_admin:
|
||||
flash('Access denied. Admin privileges required.', 'error')
|
||||
return redirect(url_for('dashboard.index'))
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
@admin_bp.route('/')
|
||||
@login_required
|
||||
@admin_required
|
||||
def index():
|
||||
users = User.query.order_by(User.created_at.desc()).all()
|
||||
return render_template('admin/dashboard.html', users=users)
|
||||
|
||||
@admin_bp.route('/users/create', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def create_user():
|
||||
username = request.form.get('username')
|
||||
password = request.form.get('password')
|
||||
is_admin = request.form.get('is_admin') == 'on'
|
||||
|
||||
if not username or not password:
|
||||
flash('Username and password are required', 'error')
|
||||
return redirect(url_for('admin.index'))
|
||||
|
||||
if User.query.filter_by(username=username).first():
|
||||
flash('Username already exists', 'error')
|
||||
return redirect(url_for('admin.index'))
|
||||
|
||||
user = User(username=username, is_admin=is_admin)
|
||||
user.set_password(password)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
flash(f'User {username} created successfully', 'success')
|
||||
return redirect(url_for('admin.index'))
|
||||
|
||||
@admin_bp.route('/users/<int:user_id>/reset-password', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def reset_password(user_id):
|
||||
user = User.query.get_or_404(user_id)
|
||||
new_password = request.form.get('new_password')
|
||||
|
||||
if not new_password:
|
||||
flash('New password is required', 'error')
|
||||
return redirect(url_for('admin.index'))
|
||||
|
||||
user.set_password(new_password)
|
||||
db.session.commit()
|
||||
|
||||
flash(f'Password reset for {user.username}', 'success')
|
||||
return redirect(url_for('admin.index'))
|
||||
|
||||
@admin_bp.route('/users/<int:user_id>/delete', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def delete_user(user_id):
|
||||
user = User.query.get_or_404(user_id)
|
||||
|
||||
if user.id == current_user.id:
|
||||
flash('Cannot delete your own account', 'error')
|
||||
return redirect(url_for('admin.index'))
|
||||
|
||||
username = user.username
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
|
||||
flash(f'User {username} deleted successfully', 'success')
|
||||
return redirect(url_for('admin.index'))
|
||||
32
app/routes/auth.py
Normal file
32
app/routes/auth.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_user, logout_user, login_required
|
||||
from app.models import User
|
||||
from app import db
|
||||
|
||||
auth_bp = Blueprint('auth', __name__)
|
||||
|
||||
@auth_bp.route('/')
|
||||
def index():
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
@auth_bp.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
username = request.form.get('username')
|
||||
password = request.form.get('password')
|
||||
|
||||
user = User.query.filter_by(username=username).first()
|
||||
|
||||
if user and user.check_password(password):
|
||||
login_user(user)
|
||||
return redirect(url_for('dashboard.index'))
|
||||
else:
|
||||
flash('Invalid username or password', 'error')
|
||||
|
||||
return render_template('login.html')
|
||||
|
||||
@auth_bp.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for('auth.login'))
|
||||
15
app/routes/dashboard.py
Normal file
15
app/routes/dashboard.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from flask import Blueprint, render_template
|
||||
from flask_login import login_required, current_user
|
||||
from app.models import Job
|
||||
|
||||
dashboard_bp = Blueprint('dashboard', __name__, url_prefix='/dashboard')
|
||||
|
||||
@dashboard_bp.route('/')
|
||||
@login_required
|
||||
def index():
|
||||
if current_user.is_admin:
|
||||
jobs = Job.query.order_by(Job.submitted_at.desc()).all()
|
||||
else:
|
||||
jobs = Job.query.filter_by(user_id=current_user.id).order_by(Job.submitted_at.desc()).all()
|
||||
|
||||
return render_template('dashboard/index.html', jobs=jobs)
|
||||
431
app/routes/jobs.py
Normal file
431
app/routes/jobs.py
Normal file
@@ -0,0 +1,431 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
from app.models import Job
|
||||
from app import db
|
||||
import json
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
jobs_bp = Blueprint('jobs', __name__, url_prefix='/jobs')
|
||||
|
||||
@jobs_bp.route('/submit')
|
||||
@login_required
|
||||
def submit():
|
||||
return render_template('jobs/submit.html')
|
||||
|
||||
@jobs_bp.route('/debug/test-step2')
|
||||
@login_required
|
||||
def debug_test_step2():
|
||||
"""Debug endpoint to test step2 template with mock data"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'error': 'Admin access required'}), 403
|
||||
|
||||
# Mock data similar to what we expect from scenario scan
|
||||
organized_data = {
|
||||
"drivers": {
|
||||
"diag_protocol_stack": ["diag_protocol_stack_init_test"]
|
||||
},
|
||||
"application_layer": {
|
||||
"data_pool": ["data_pool_init_test"],
|
||||
"actuator_manager": ["actuator_manager_init_test2", "actuator_manager_init_test"],
|
||||
"event_system": ["event_system_init_test"]
|
||||
}
|
||||
}
|
||||
|
||||
scenario_map = {
|
||||
"diag_protocol_stack_init_test": "drivers/diag_protocol_stack/test/diag_protocol_stack_init_test.test_scenario.xml",
|
||||
"data_pool_init_test": "application_layer/DP_stack/data_pool/test/data_pool_init_test.test_scenario.xml",
|
||||
"actuator_manager_init_test2": "application_layer/business_stack/actuator_manager/test/actuator_manager_init_test2.test_scenario.xml",
|
||||
"actuator_manager_init_test": "application_layer/business_stack/actuator_manager/test/actuator_manager_init_test.test_scenario.xml",
|
||||
"event_system_init_test": "application_layer/business_stack/event_system/test/event_system_init_test.test_scenario.xml"
|
||||
}
|
||||
|
||||
try:
|
||||
return render_template('jobs/submit_step2.html',
|
||||
branch_name='test_branch',
|
||||
organized_data=organized_data,
|
||||
scenario_map=scenario_map)
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'error': str(e),
|
||||
'type': type(e).__name__,
|
||||
'template_exists': True
|
||||
})
|
||||
|
||||
@jobs_bp.route('/debug/ssh-test')
|
||||
@login_required
|
||||
def debug_ssh_test():
|
||||
"""Debug endpoint to test SSH connectivity"""
|
||||
if not current_user.is_admin:
|
||||
return jsonify({'error': 'Admin access required'}), 403
|
||||
|
||||
try:
|
||||
ssh_password = os.environ.get('SSH_PASSWORD', 'default_password')
|
||||
ssh_host = os.environ.get('SSH_HOST', 'remote_host')
|
||||
ssh_user = os.environ.get('SSH_USER', 'asf')
|
||||
ssh_port = os.environ.get('SSH_PORT', '22')
|
||||
|
||||
# SSH options for better connectivity
|
||||
ssh_options = f"-p {ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
|
||||
# Test basic SSH connectivity
|
||||
test_cmd = f"sshpass -p '{ssh_password}' ssh {ssh_options} {ssh_user}@{ssh_host} 'echo SSH_CONNECTION_SUCCESS'"
|
||||
result = subprocess.run(test_cmd, shell=True, capture_output=True, text=True, timeout=30)
|
||||
|
||||
return jsonify({
|
||||
'ssh_config': {
|
||||
'user': ssh_user,
|
||||
'host': ssh_host,
|
||||
'port': ssh_port,
|
||||
'password_set': bool(ssh_password and ssh_password != 'default_password')
|
||||
},
|
||||
'command_executed': test_cmd.replace(ssh_password, '***'),
|
||||
'test_result': {
|
||||
'returncode': result.returncode,
|
||||
'stdout': result.stdout,
|
||||
'stderr': result.stderr
|
||||
},
|
||||
'success': result.returncode == 0 and 'SSH_CONNECTION_SUCCESS' in result.stdout
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'error': str(e),
|
||||
'success': False
|
||||
})
|
||||
|
||||
@jobs_bp.route('/submit/step1', methods=['POST'])
|
||||
@login_required
|
||||
def submit_step1():
|
||||
branch_name = request.form.get('branch_name')
|
||||
|
||||
if not branch_name:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Branch name is required',
|
||||
'output': ''
|
||||
})
|
||||
|
||||
# Validate branch exists on remote
|
||||
try:
|
||||
# Get SSH configuration from environment variables
|
||||
ssh_password = os.environ.get('SSH_PASSWORD', 'default_password')
|
||||
ssh_host = os.environ.get('SSH_HOST', 'remote_host')
|
||||
ssh_user = os.environ.get('SSH_USER', 'asf')
|
||||
ssh_port = os.environ.get('SSH_PORT', '22')
|
||||
|
||||
print(f"[DEBUG] Starting branch validation for: {branch_name}")
|
||||
print(f"[DEBUG] SSH Config - User: {ssh_user}, Host: {ssh_host}, Port: {ssh_port}")
|
||||
|
||||
# SSH options for better connectivity
|
||||
ssh_options = f"-p {ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
|
||||
|
||||
# First, clone the repository
|
||||
clone_cmd = f"sshpass -p '{ssh_password}' ssh {ssh_options} {ssh_user}@{ssh_host} './TPF/gitea_repo_controller.sh clone'"
|
||||
print(f"[DEBUG] Executing clone command: {clone_cmd.replace(ssh_password, '***')}")
|
||||
|
||||
clone_result = subprocess.run(clone_cmd, shell=True, capture_output=True, text=True, timeout=60)
|
||||
|
||||
print(f"[DEBUG] Clone result - Return code: {clone_result.returncode}")
|
||||
print(f"[DEBUG] Clone stdout: {clone_result.stdout}")
|
||||
print(f"[DEBUG] Clone stderr: {clone_result.stderr}")
|
||||
|
||||
# Then, checkout the branch
|
||||
checkout_cmd = f"sshpass -p '{ssh_password}' ssh {ssh_options} {ssh_user}@{ssh_host} './TPF/gitea_repo_controller.sh checkout {branch_name}'"
|
||||
print(f"[DEBUG] Executing checkout command: {checkout_cmd.replace(ssh_password, '***')}")
|
||||
|
||||
checkout_result = subprocess.run(checkout_cmd, shell=True, capture_output=True, text=True, timeout=60)
|
||||
|
||||
print(f"[DEBUG] Checkout result - Return code: {checkout_result.returncode}")
|
||||
print(f"[DEBUG] Checkout stdout: {checkout_result.stdout}")
|
||||
print(f"[DEBUG] Checkout stderr: {checkout_result.stderr}")
|
||||
|
||||
# Combine all output for analysis
|
||||
full_output = f"CLONE OUTPUT:\n{clone_result.stdout}\n{clone_result.stderr}\n\nCHECKOUT OUTPUT:\n{checkout_result.stdout}\n{checkout_result.stderr}"
|
||||
|
||||
# Check if checkout was successful
|
||||
# Look for "fatal:" in the output which indicates failure
|
||||
checkout_failed = (
|
||||
"fatal:" in checkout_result.stdout.lower() or
|
||||
"fatal:" in checkout_result.stderr.lower() or
|
||||
checkout_result.returncode != 0
|
||||
)
|
||||
|
||||
print(f"[DEBUG] Checkout failed: {checkout_failed}")
|
||||
|
||||
if checkout_failed:
|
||||
error_msg = f'Branch "{branch_name}" not found on remote. Please push the branch first.'
|
||||
|
||||
# Try to extract more specific error from output
|
||||
if "fatal:" in checkout_result.stdout:
|
||||
fatal_line = [line for line in checkout_result.stdout.split('\n') if 'fatal:' in line.lower()]
|
||||
if fatal_line:
|
||||
error_msg += f" Error: {fatal_line[0].strip()}"
|
||||
elif "fatal:" in checkout_result.stderr:
|
||||
fatal_line = [line for line in checkout_result.stderr.split('\n') if 'fatal:' in line.lower()]
|
||||
if fatal_line:
|
||||
error_msg += f" Error: {fatal_line[0].strip()}"
|
||||
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': error_msg,
|
||||
'output': full_output,
|
||||
'debug': {
|
||||
'clone_returncode': clone_result.returncode,
|
||||
'checkout_returncode': checkout_result.returncode,
|
||||
'branch_name': branch_name
|
||||
}
|
||||
})
|
||||
|
||||
# If successful, run scenario scan to get available scenarios
|
||||
print(f"[DEBUG] Running scenario scan...")
|
||||
scan_cmd = f"sshpass -p '{ssh_password}' ssh {ssh_options} {ssh_user}@{ssh_host} 'python3 TPF/Sensor_hub_repo/Tools/TPF/scenario_scan.py'"
|
||||
print(f"[DEBUG] Executing scan command: {scan_cmd.replace(ssh_password, '***')}")
|
||||
|
||||
scan_result = subprocess.run(scan_cmd, shell=True, capture_output=True, text=True, timeout=120)
|
||||
|
||||
print(f"[DEBUG] Scan result - Return code: {scan_result.returncode}")
|
||||
print(f"[DEBUG] Scan stdout: {scan_result.stdout}")
|
||||
print(f"[DEBUG] Scan stderr: {scan_result.stderr}")
|
||||
|
||||
# Parse the JSON response from scenario scan
|
||||
try:
|
||||
scan_data = json.loads(scan_result.stdout)
|
||||
organized_data = scan_data.get('organized_data', {})
|
||||
scenario_map = scan_data.get('scenario_map', {})
|
||||
|
||||
print(f"[DEBUG] Found {len(scenario_map)} scenarios in {len(organized_data)} categories")
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"[ERROR] Failed to parse scenario scan JSON: {e}")
|
||||
print(f"[ERROR] Raw output: {scan_result.stdout}")
|
||||
# Fallback to mock data if scan fails
|
||||
organized_data = {
|
||||
"drivers": {
|
||||
"diag_protocol_stack": ["diag_protocol_stack_init_test"]
|
||||
},
|
||||
"application_layer": {
|
||||
"data_pool": ["data_pool_init_test"],
|
||||
"actuator_manager": ["actuator_manager_init_test2", "actuator_manager_init_test"],
|
||||
"event_system": ["event_system_init_test"]
|
||||
}
|
||||
}
|
||||
scenario_map = {
|
||||
"diag_protocol_stack_init_test": "drivers/diag_protocol_stack/test/diag_protocol_stack_init_test.test_scenario.xml",
|
||||
"data_pool_init_test": "application_layer/DP_stack/data_pool/test/data_pool_init_test.test_scenario.xml",
|
||||
"actuator_manager_init_test2": "application_layer/business_stack/actuator_manager/test/actuator_manager_init_test2.test_scenario.xml",
|
||||
"actuator_manager_init_test": "application_layer/business_stack/actuator_manager/test/actuator_manager_init_test.test_scenario.xml",
|
||||
"event_system_init_test": "application_layer/business_stack/event_system/test/event_system_init_test.test_scenario.xml"
|
||||
}
|
||||
|
||||
print(f"[DEBUG] Branch validation successful for: {branch_name}")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'organized_data': organized_data,
|
||||
'scenario_map': scenario_map,
|
||||
'message': f'Branch "{branch_name}" validated successfully',
|
||||
'output': full_output,
|
||||
'debug': {
|
||||
'clone_returncode': clone_result.returncode,
|
||||
'checkout_returncode': checkout_result.returncode,
|
||||
'scan_returncode': scan_result.returncode if 'scan_result' in locals() else 'N/A',
|
||||
'branch_name': branch_name,
|
||||
'scenarios_found': len(scenario_map)
|
||||
}
|
||||
})
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
error_msg = 'Branch validation timed out. Please try again.'
|
||||
print(f"[ERROR] {error_msg}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': error_msg,
|
||||
'output': 'Command timed out after 60 seconds'
|
||||
})
|
||||
except Exception as e:
|
||||
error_msg = f'Error validating branch: {str(e)}'
|
||||
print(f"[ERROR] {error_msg}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': error_msg,
|
||||
'output': f'Exception: {str(e)}'
|
||||
})
|
||||
|
||||
@jobs_bp.route('/submit/step2', methods=['POST'])
|
||||
@login_required
|
||||
def submit_step2():
|
||||
branch_name = request.form.get('branch_name')
|
||||
scenario_map_json = request.form.get('scenario_map')
|
||||
selected_scenarios_json = request.form.get('selected_scenarios')
|
||||
|
||||
try:
|
||||
scenario_map = json.loads(scenario_map_json) if scenario_map_json else {}
|
||||
selected_scenarios = json.loads(selected_scenarios_json) if selected_scenarios_json else []
|
||||
except json.JSONDecodeError:
|
||||
flash('Invalid scenario data', 'error')
|
||||
return redirect(url_for('jobs.submit'))
|
||||
|
||||
if not selected_scenarios:
|
||||
flash('Please select at least one scenario', 'error')
|
||||
return redirect(url_for('jobs.submit'))
|
||||
|
||||
return render_template('jobs/submit_step3.html',
|
||||
branch_name=branch_name,
|
||||
scenarios=selected_scenarios,
|
||||
scenario_map=scenario_map)
|
||||
|
||||
@jobs_bp.route('/submit/step2-validated', methods=['POST'])
|
||||
@login_required
|
||||
def submit_step2_validated():
|
||||
try:
|
||||
branch_name = request.form.get('branch_name')
|
||||
organized_data_json = request.form.get('organized_data')
|
||||
scenario_map_json = request.form.get('scenario_map')
|
||||
|
||||
print(f"[DEBUG] Step2 - Branch: {branch_name}")
|
||||
print(f"[DEBUG] Step2 - Organized data length: {len(organized_data_json) if organized_data_json else 0}")
|
||||
print(f"[DEBUG] Step2 - Scenario map length: {len(scenario_map_json) if scenario_map_json else 0}")
|
||||
|
||||
if not branch_name:
|
||||
print("[ERROR] Step2 - No branch name provided")
|
||||
flash('Branch name is required', 'error')
|
||||
return redirect(url_for('jobs.submit'))
|
||||
|
||||
try:
|
||||
organized_data = json.loads(organized_data_json) if organized_data_json else {}
|
||||
scenario_map = json.loads(scenario_map_json) if scenario_map_json else {}
|
||||
|
||||
print(f"[DEBUG] Step2 - Parsed organized_data keys: {list(organized_data.keys())}")
|
||||
print(f"[DEBUG] Step2 - Parsed scenario_map count: {len(scenario_map)}")
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"[ERROR] Step2 - JSON decode error: {e}")
|
||||
flash('Invalid scenario data', 'error')
|
||||
return redirect(url_for('jobs.submit'))
|
||||
|
||||
print(f"[DEBUG] Step2 - Rendering template with {len(organized_data)} layers")
|
||||
|
||||
return render_template('jobs/submit_step2.html',
|
||||
branch_name=branch_name,
|
||||
organized_data=organized_data,
|
||||
scenario_map=scenario_map)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Step2 - Unexpected error: {str(e)}")
|
||||
print(f"[ERROR] Step2 - Error type: {type(e).__name__}")
|
||||
import traceback
|
||||
print(f"[ERROR] Step2 - Traceback: {traceback.format_exc()}")
|
||||
flash(f'Error loading scenario selection: {str(e)}', 'error')
|
||||
return redirect(url_for('jobs.submit'))
|
||||
@login_required
|
||||
def submit_step2():
|
||||
branch_name = request.form.get('branch_name')
|
||||
selected_scenarios = request.form.getlist('scenarios')
|
||||
|
||||
if not selected_scenarios:
|
||||
flash('Please select at least one scenario', 'error')
|
||||
return redirect(url_for('jobs.submit'))
|
||||
|
||||
return render_template('jobs/submit_step3.html',
|
||||
branch_name=branch_name,
|
||||
scenarios=selected_scenarios)
|
||||
|
||||
@jobs_bp.route('/submit/step3', methods=['POST'])
|
||||
@login_required
|
||||
def submit_step3():
|
||||
branch_name = request.form.get('branch_name')
|
||||
scenarios_json = request.form.get('scenarios')
|
||||
scenario_map_json = request.form.get('scenario_map')
|
||||
environment = request.form.get('environment')
|
||||
|
||||
try:
|
||||
scenarios = json.loads(scenarios_json) if scenarios_json else []
|
||||
scenario_map = json.loads(scenario_map_json) if scenario_map_json else {}
|
||||
except json.JSONDecodeError:
|
||||
flash('Invalid scenario data', 'error')
|
||||
return redirect(url_for('jobs.submit'))
|
||||
|
||||
return render_template('jobs/submit_step4.html',
|
||||
branch_name=branch_name,
|
||||
scenarios=scenarios,
|
||||
scenario_map=scenario_map,
|
||||
environment=environment)
|
||||
|
||||
@jobs_bp.route('/submit/final', methods=['POST'])
|
||||
@login_required
|
||||
def submit_final():
|
||||
branch_name = request.form.get('branch_name')
|
||||
scenarios_json = request.form.get('scenarios')
|
||||
scenario_map_json = request.form.get('scenario_map')
|
||||
environment = request.form.get('environment')
|
||||
test_mode = request.form.get('test_mode')
|
||||
keep_devbenches = request.form.get('keep_devbenches') == 'on'
|
||||
reuse_results = request.form.get('reuse_results') == 'on'
|
||||
|
||||
try:
|
||||
scenarios = json.loads(scenarios_json) if scenarios_json else []
|
||||
scenario_map = json.loads(scenario_map_json) if scenario_map_json else {}
|
||||
except json.JSONDecodeError:
|
||||
flash('Invalid scenario data', 'error')
|
||||
return redirect(url_for('jobs.submit'))
|
||||
|
||||
job = Job(
|
||||
user_id=current_user.id,
|
||||
branch_name=branch_name,
|
||||
scenarios=json.dumps(scenarios), # Store as JSON string
|
||||
environment=environment,
|
||||
test_mode=test_mode,
|
||||
keep_devbenches=keep_devbenches,
|
||||
reuse_results=reuse_results,
|
||||
status='in_progress'
|
||||
)
|
||||
|
||||
db.session.add(job)
|
||||
db.session.commit()
|
||||
|
||||
# TODO: Start test execution in background using scenario_map
|
||||
|
||||
flash('Test job submitted successfully', 'success')
|
||||
return redirect(url_for('dashboard.index'))
|
||||
|
||||
@jobs_bp.route('/<int:job_id>')
|
||||
@login_required
|
||||
def view_job(job_id):
|
||||
job = Job.query.get_or_404(job_id)
|
||||
|
||||
if not current_user.is_admin and job.user_id != current_user.id:
|
||||
flash('Access denied', 'error')
|
||||
return redirect(url_for('dashboard.index'))
|
||||
|
||||
return jsonify({
|
||||
'id': job.id,
|
||||
'submitter': job.submitter.username,
|
||||
'branch_name': job.branch_name,
|
||||
'scenarios': job.scenarios,
|
||||
'environment': job.environment,
|
||||
'test_mode': job.test_mode,
|
||||
'status': job.status,
|
||||
'submitted_at': job.submitted_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'completed_at': job.completed_at.strftime('%Y-%m-%d %H:%M:%S') if job.completed_at else None,
|
||||
'duration': job.duration,
|
||||
'keep_devbenches': job.keep_devbenches,
|
||||
'reuse_results': job.reuse_results,
|
||||
'results_path': job.results_path
|
||||
})
|
||||
|
||||
@jobs_bp.route('/<int:job_id>/abort', methods=['POST'])
|
||||
@login_required
|
||||
def abort_job(job_id):
|
||||
job = Job.query.get_or_404(job_id)
|
||||
|
||||
if not current_user.is_admin and job.user_id != current_user.id:
|
||||
return jsonify({'error': 'Access denied'}), 403
|
||||
|
||||
if job.status == 'in_progress':
|
||||
job.status = 'aborted'
|
||||
db.session.commit()
|
||||
# TODO: Kill the running process
|
||||
return jsonify({'success': True})
|
||||
|
||||
return jsonify({'error': 'Job is not in progress'}), 400
|
||||
Reference in New Issue
Block a user