from typing import Dict, Optional, List, Union from datetime import datetime from sqlalchemy.orm import Session import re from models.project import Project from schemas.project import ProjectCreate, ProjectUpdate def validate_github_link(github_link: str) -> bool: """ Validate GitHub repository URL format. Args: github_link: GitHub URL to validate Returns: bool: True if URL format is valid, False otherwise """ pattern = r'^https?:\/\/github\.com\/[\w-]+\/[\w-]+(?:\.git)?$' return bool(re.match(pattern, github_link)) def validate_project_data(project_data: ProjectCreate) -> Dict[str, str]: """ Validate project data before creation. Args: project_data: Project data to validate Returns: Dict containing validation errors if any """ errors = {} if len(project_data.title) > 255: errors["title"] = "Title must not exceed 255 characters" if not project_data.description: errors["description"] = "Description is required" if project_data.year < 2000 or project_data.year > datetime.now().year: errors["year"] = "Invalid year" if project_data.github_link and not validate_github_link(project_data.github_link): errors["github_link"] = "Invalid GitHub repository URL" return errors def get_projects_by_technology(db: Session, technology: str) -> List[Project]: """ Get all projects that use a specific technology. Args: db: Database session technology: Technology to search for Returns: List of projects using the specified technology """ return db.query(Project).filter(Project.technologies_used.ilike(f'%{technology}%')).all() def get_department_statistics(db: Session) -> Dict[str, Dict[str, Union[int, float]]]: """ Get project statistics by department. Args: db: Database session Returns: Dictionary containing department statistics """ departments = db.query(Project.department).distinct().all() stats = {} for dept in departments: dept_name = dept[0] dept_projects = db.query(Project).filter(Project.department == dept_name).all() published_count = sum(1 for p in dept_projects if p.is_published) avg_grade = sum(float(p.grade) for p in dept_projects if p.grade) / len(dept_projects) if dept_projects else 0 stats[dept_name] = { "total_projects": len(dept_projects), "published_projects": published_count, "average_grade": round(avg_grade, 2) } return stats def format_project_response(project: Project) -> Dict[str, Any]: """ Format project data for API response. Args: project: Project object to format Returns: Formatted project data dictionary """ technologies = project.technologies_used.split(',') if project.technologies_used else [] return { "id": project.id, "title": project.title, "description": project.description, "student_name": project.student_name, "supervisor_name": project.supervisor_name, "department": project.department, "year": project.year, "status": project.status, "technologies": technologies, "github_link": project.github_link, "documentation_link": project.documentation_link, "grade": project.grade, "is_published": project.is_published, "created_at": project.created_at.isoformat() if project.created_at else None, "updated_at": project.updated_at.isoformat() if project.updated_at else None }