""" Project Manager Module Handles project storage, loading, and management """ import json import os from datetime import datetime from typing import List, Dict, Optional import uuid from utils.logger import logger class Project: def __init__(self, name: str, path: str, project_id: str = None): self.id = project_id or str(uuid.uuid4()) self.name = name self.path = path self.created_at = datetime.now().isoformat() self.last_accessed = datetime.now().isoformat() self.readme_path = os.path.join(path, "CLAUDE_PROJECT_README.md") self.description = "" self.tags = [] self.gitea_repo = None # Format: "owner/repo_name" or None if not linked def to_dict(self) -> Dict: """Convert project to dictionary for JSON storage""" return { 'id': self.id, 'name': self.name, 'path': self.path, 'created_at': self.created_at, 'last_accessed': self.last_accessed, 'readme_path': self.readme_path, 'description': self.description, 'tags': self.tags, 'gitea_repo': self.gitea_repo } @classmethod def from_dict(cls, data: Dict) -> 'Project': """Create project from dictionary""" project = cls(data['name'], data['path'], data['id']) project.created_at = data.get('created_at', datetime.now().isoformat()) project.last_accessed = data.get('last_accessed', datetime.now().isoformat()) project.readme_path = data.get('readme_path', os.path.join(data['path'], "CLAUDE_PROJECT_README.md")) project.description = data.get('description', '') project.tags = data.get('tags', []) project.gitea_repo = data.get('gitea_repo', None) return project def update_last_accessed(self): """Update last accessed timestamp""" self.last_accessed = datetime.now().isoformat() class ProjectManager: def __init__(self, data_file: str = "data/projects.json"): self.data_file = data_file self.projects: Dict[str, Project] = {} self.vps_project = None self.admin_panel_project = None self.activity_server_project = None self._ensure_data_dir() self.load_projects() self._initialize_vps_project() self._initialize_admin_panel_project() self._initialize_activity_server_project() def _ensure_data_dir(self): """Ensure data directory exists""" data_dir = os.path.dirname(self.data_file) if data_dir and not os.path.exists(data_dir): os.makedirs(data_dir) def _initialize_vps_project(self): """Initialize the permanent VPS project""" vps_id = "vps-permanent" if vps_id not in self.projects: self.vps_project = Project( name="VPS Server", path="claude-dev@91.99.192.14", project_id=vps_id ) self.vps_project.description = "Remote VPS Server with Claude" self.vps_project.tags = ["vps", "remote", "server"] self.projects[vps_id] = self.vps_project self.save_projects() else: self.vps_project = self.projects[vps_id] def _initialize_admin_panel_project(self): """Initialize the permanent Admin Panel project""" admin_id = "admin-panel-permanent" if admin_id not in self.projects: self.admin_panel_project = Project( name="Admin Panel", path="/opt/v2-Docker", project_id=admin_id ) self.admin_panel_project.description = "V2 Docker Admin Panel" self.admin_panel_project.tags = ["admin", "docker", "v2"] self.projects[admin_id] = self.admin_panel_project self.save_projects() else: self.admin_panel_project = self.projects[admin_id] def _initialize_activity_server_project(self): """Initialize the permanent Activity Server project""" activity_id = "activity-server-permanent" if activity_id not in self.projects: self.activity_server_project = Project( name="Activity Server", path="/home/claude-dev/cpm-activity-server", project_id=activity_id ) self.activity_server_project.description = "CPM Activity Server" self.activity_server_project.tags = ["activity", "server", "cpm"] self.projects[activity_id] = self.activity_server_project self.save_projects() else: self.activity_server_project = self.projects[activity_id] def load_projects(self): """Load projects from JSON file""" logger.info("Loading projects from file") if os.path.exists(self.data_file): try: with open(self.data_file, 'r', encoding='utf-8') as f: data = json.load(f) for proj_data in data.get('projects', []): project = Project.from_dict(proj_data) self.projects[project.id] = project except Exception as e: print(f"Error loading projects: {e}") self.projects = {} def save_projects(self): """Save projects to JSON file""" logger.debug("Saving projects to file") try: data = { 'projects': [proj.to_dict() for proj in self.projects.values()], 'last_updated': datetime.now().isoformat() } with open(self.data_file, 'w', encoding='utf-8') as f: json.dump(data, f, indent=2, ensure_ascii=False) except Exception as e: print(f"Error saving projects: {e}") def add_project(self, name: str, path: str) -> Project: """Add new project""" logger.info(f"Adding project: {name} at {path}") # Check if project with same path already exists for project in self.projects.values(): if project.path == path and project.id != "vps-permanent": # Update existing project project.name = name project.update_last_accessed() self.save_projects() return project # Create new project project = Project(name, path) self.projects[project.id] = project self.save_projects() return project def remove_project(self, project_id: str) -> bool: """Remove project by ID""" logger.info(f"Removing project with ID: {project_id}") if project_id in self.projects and project_id not in ["vps-permanent", "admin-panel-permanent"]: del self.projects[project_id] self.save_projects() return True return False def get_project(self, project_id: str) -> Optional[Project]: """Get project by ID""" return self.projects.get(project_id) def get_all_projects(self) -> List[Project]: """Get all projects sorted alphabetically by name""" projects = list(self.projects.values()) # Sort alphabetically, but keep VPS projects first vps = [p for p in projects if p.id == "vps-permanent"] admin = [p for p in projects if p.id == "admin-panel-permanent"] activity = [p for p in projects if p.id == "activity-server-permanent"] others = [p for p in projects if p.id not in ["vps-permanent", "admin-panel-permanent", "activity-server-permanent"]] others.sort(key=lambda p: p.name.lower()) # Sort alphabetically by name (case-insensitive) return vps + admin + activity + others def update_project(self, project_id: str, **kwargs): """Update project properties""" project = self.get_project(project_id) if project: for key, value in kwargs.items(): if hasattr(project, key): setattr(project, key, value) self.save_projects() def search_projects(self, query: str) -> List[Project]: """Search projects by name, path, or tags""" query = query.lower() results = [] for project in self.projects.values(): if (query in project.name.lower() or query in project.path.lower() or any(query in tag.lower() for tag in project.tags)): results.append(project) return results # Test the module if __name__ == "__main__": # Create manager manager = ProjectManager("test_projects.json") # Add test projects proj1 = manager.add_project("Test Project 1", "C:\\Users\\test\\project1") proj2 = manager.add_project("Test Project 2", "C:\\Users\\test\\project2") # List all projects print("All projects:") for project in manager.get_all_projects(): print(f"- {project.name} ({project.path})") # Clean up test file import os if os.path.exists("test_projects.json"): os.remove("test_projects.json")