234 lines
9.3 KiB
Python
234 lines
9.3 KiB
Python
"""
|
|
Models package for ScriptsManager
|
|
Contains all database models and related classes
|
|
"""
|
|
|
|
from app.models.user import User, UserRole
|
|
from app.models.script import Script, ScriptGroup
|
|
|
|
# Import model classes that were defined in this file originally
|
|
from datetime import datetime
|
|
from app.config.database import db
|
|
|
|
|
|
class UserProject(db.Model):
|
|
"""User project model for organizing user work."""
|
|
|
|
__tablename__ = "user_projects"
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
|
|
project_name = db.Column(db.String(100), nullable=False)
|
|
group_id = db.Column(db.Integer, db.ForeignKey("script_groups.id"), nullable=False)
|
|
description = db.Column(db.Text)
|
|
is_default = db.Column(db.Boolean, default=False)
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
last_accessed = db.Column(db.DateTime)
|
|
|
|
def to_dict(self):
|
|
"""Convert user project to dictionary."""
|
|
return {
|
|
"id": self.id,
|
|
"user_id": self.user_id,
|
|
"project_name": self.project_name,
|
|
"group_id": self.group_id,
|
|
"description": self.description,
|
|
"is_default": self.is_default,
|
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
|
"last_accessed": (
|
|
self.last_accessed.isoformat() if self.last_accessed else None
|
|
),
|
|
}
|
|
|
|
|
|
class UserScriptTag(db.Model):
|
|
"""User script tags for personal organization."""
|
|
|
|
__tablename__ = "user_script_tags"
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
|
|
script_id = db.Column(db.Integer, db.ForeignKey("scripts.id"), nullable=False)
|
|
tags = db.Column(db.Text) # Comma-separated user-specific tags
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
|
|
__table_args__ = (db.UniqueConstraint("user_id", "script_id"),)
|
|
|
|
def get_tags_list(self):
|
|
"""Get tags as list."""
|
|
if self.tags:
|
|
return [tag.strip() for tag in self.tags.split(",") if tag.strip()]
|
|
return []
|
|
|
|
def set_tags_list(self, tags_list):
|
|
"""Set tags from list."""
|
|
self.tags = ",".join(tags_list) if tags_list else None
|
|
|
|
|
|
class CondaEnvironment(db.Model):
|
|
"""Conda environment model."""
|
|
|
|
__tablename__ = "conda_environments"
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
name = db.Column(db.String(100), unique=True, nullable=False)
|
|
path = db.Column(db.String(255), nullable=False)
|
|
python_version = db.Column(db.String(20))
|
|
is_available = db.Column(db.Boolean, default=True)
|
|
detected_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
last_verified = db.Column(db.DateTime)
|
|
|
|
def to_dict(self):
|
|
"""Convert conda environment to dictionary."""
|
|
return {
|
|
"id": self.id,
|
|
"name": self.name,
|
|
"path": self.path,
|
|
"python_version": self.python_version,
|
|
"is_available": self.is_available,
|
|
"detected_at": self.detected_at.isoformat() if self.detected_at else None,
|
|
"last_verified": (
|
|
self.last_verified.isoformat() if self.last_verified else None
|
|
),
|
|
}
|
|
|
|
|
|
class ExecutionLog(db.Model):
|
|
"""Execution log model for tracking script runs."""
|
|
|
|
__tablename__ = "execution_logs"
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
script_id = db.Column(db.Integer, db.ForeignKey("scripts.id"), nullable=False)
|
|
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
|
|
project_id = db.Column(db.Integer, db.ForeignKey("user_projects.id"), nullable=True) # Added project support
|
|
status = db.Column(db.String(20), nullable=False)
|
|
start_time = db.Column(db.DateTime, default=datetime.utcnow)
|
|
end_time = db.Column(db.DateTime)
|
|
output = db.Column(db.Text)
|
|
error_output = db.Column(db.Text)
|
|
exit_code = db.Column(db.Integer)
|
|
# Debug fields
|
|
command_executed = db.Column(db.Text) # Full command that was executed
|
|
conda_environment = db.Column(db.String(100)) # Conda env used
|
|
working_directory = db.Column(db.String(500)) # Working directory
|
|
process_id = db.Column(db.Integer) # Process ID
|
|
port_allocated = db.Column(db.Integer) # Port allocated to script
|
|
# Proxy-specific fields
|
|
proxy_port = db.Column(db.Integer) # Internal proxy port
|
|
proxy_url = db.Column(db.String(255)) # Proxy URL path
|
|
execution_mode = db.Column(db.String(20), default='proxy') # 'proxy', 'direct', 'celery'
|
|
|
|
def to_dict(self):
|
|
"""Convert execution log to dictionary."""
|
|
return {
|
|
"id": self.id,
|
|
"script_id": self.script_id,
|
|
"user_id": self.user_id,
|
|
"project_id": self.project_id,
|
|
"status": self.status,
|
|
"start_time": self.start_time.isoformat() if self.start_time else None,
|
|
"end_time": self.end_time.isoformat() if self.end_time else None,
|
|
"output": self.output,
|
|
"error_output": self.error_output,
|
|
"exit_code": self.exit_code,
|
|
"command_executed": self.command_executed,
|
|
"conda_environment": self.conda_environment,
|
|
"working_directory": self.working_directory,
|
|
"process_id": self.process_id,
|
|
"port_allocated": self.port_allocated,
|
|
"proxy_port": self.proxy_port,
|
|
"proxy_url": self.proxy_url,
|
|
"execution_mode": self.execution_mode,
|
|
}
|
|
|
|
|
|
class ScriptProxyExecution(db.Model):
|
|
"""Proxy execution model for tracking running script instances."""
|
|
|
|
__tablename__ = "script_proxy_executions"
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
script_id = db.Column(db.Integer, db.ForeignKey("scripts.id"), nullable=False)
|
|
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
|
|
project_id = db.Column(db.Integer, db.ForeignKey("user_projects.id"), nullable=False)
|
|
|
|
# Proxy configuration
|
|
internal_port = db.Column(db.Integer, nullable=False, unique=True)
|
|
proxy_path = db.Column(db.String(255), nullable=False) # /project/{pid}/script/{sid}/user/{uid}
|
|
workspace_path = db.Column(db.String(500), nullable=False)
|
|
|
|
# Execution details
|
|
process_id = db.Column(db.Integer)
|
|
status = db.Column(db.String(20), default='starting') # starting, running, stopping, stopped, error
|
|
conda_environment = db.Column(db.String(100))
|
|
|
|
# Timing
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
|
started_at = db.Column(db.DateTime)
|
|
last_activity = db.Column(db.DateTime, default=datetime.utcnow)
|
|
stopped_at = db.Column(db.DateTime)
|
|
|
|
# Metadata
|
|
script_name = db.Column(db.String(100))
|
|
parameters = db.Column(db.Text) # JSON string of parameters
|
|
environment_vars = db.Column(db.Text) # JSON string of env vars
|
|
|
|
# Relationships
|
|
script = db.relationship("Script", backref="proxy_executions")
|
|
user = db.relationship("User", backref="proxy_executions")
|
|
project = db.relationship("UserProject", backref="proxy_executions")
|
|
|
|
__table_args__ = (
|
|
db.UniqueConstraint("project_id", "script_id", "user_id", name="unique_project_script_user"),
|
|
)
|
|
|
|
@property
|
|
def script_key(self) -> str:
|
|
"""Generate unique script key."""
|
|
return f"{self.project_id}_{self.script_id}_{self.user_id}"
|
|
|
|
@property
|
|
def is_active(self) -> bool:
|
|
"""Check if script is active (running or recently active)."""
|
|
if self.status not in ['running']:
|
|
return False
|
|
if not self.last_activity:
|
|
return False
|
|
return (datetime.utcnow() - self.last_activity).total_seconds() < 300 # 5 minutes
|
|
|
|
def update_activity(self):
|
|
"""Update last activity timestamp."""
|
|
self.last_activity = datetime.utcnow()
|
|
|
|
def to_dict(self):
|
|
"""Convert to dictionary."""
|
|
return {
|
|
"id": self.id,
|
|
"script_id": self.script_id,
|
|
"user_id": self.user_id,
|
|
"project_id": self.project_id,
|
|
"internal_port": self.internal_port,
|
|
"proxy_path": self.proxy_path,
|
|
"workspace_path": self.workspace_path,
|
|
"process_id": self.process_id,
|
|
"status": self.status,
|
|
"conda_environment": self.conda_environment,
|
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
|
"started_at": self.started_at.isoformat() if self.started_at else None,
|
|
"last_activity": self.last_activity.isoformat() if self.last_activity else None,
|
|
"stopped_at": self.stopped_at.isoformat() if self.stopped_at else None,
|
|
"script_name": self.script_name,
|
|
"parameters": self.parameters,
|
|
"environment_vars": self.environment_vars,
|
|
"script_key": self.script_key,
|
|
"is_active": self.is_active,
|
|
}
|
|
|
|
|
|
# Make sure all models are imported
|
|
__all__ = [
|
|
'User', 'UserRole', 'Script', 'ScriptGroup', 'UserProject',
|
|
'UserScriptTag', 'CondaEnvironment', 'ExecutionLog', 'ScriptProxyExecution'
|
|
]
|