From 808de0bdd87c8e2946c747c603ac5e4a7f80a275 Mon Sep 17 00:00:00 2001 From: Backend IM Bot Date: Thu, 27 Mar 2025 17:57:27 +0000 Subject: [PATCH] Add helper functions for Project --- helpers/project_helpers.py | 116 +++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 helpers/project_helpers.py diff --git a/helpers/project_helpers.py b/helpers/project_helpers.py new file mode 100644 index 0000000..5f09fdc --- /dev/null +++ b/helpers/project_helpers.py @@ -0,0 +1,116 @@ +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 + } \ No newline at end of file