Init Repo
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Validation script to verify migration results and file accessibility.
|
||||
|
||||
This script validates that:
|
||||
1. All file paths in the database are now relative
|
||||
2. All files are accessible via their relative paths
|
||||
3. File serving endpoints will work correctly
|
||||
|
||||
Requirements addressed: 1.5, 4.3, 4.5
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
|
||||
# Add the backend directory to the path
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from database import engine
|
||||
from models.task import Submission, TaskAttachment
|
||||
from models.project import Project
|
||||
|
||||
|
||||
class MigrationValidator:
|
||||
"""Validates migration results and file accessibility."""
|
||||
|
||||
def __init__(self):
|
||||
self.SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
self.backend_dir = Path(__file__).parent.resolve()
|
||||
self.issues = []
|
||||
|
||||
def is_relative_path(self, path: str) -> bool:
|
||||
"""Check if a path is relative."""
|
||||
if not path:
|
||||
return False
|
||||
return not Path(path).is_absolute()
|
||||
|
||||
def validate_file_accessibility(self, relative_path: str) -> Tuple[bool, str]:
|
||||
"""Validate that a file is accessible via its relative path."""
|
||||
try:
|
||||
full_path = self.backend_dir / relative_path
|
||||
exists = full_path.exists()
|
||||
return exists, str(full_path)
|
||||
except Exception as e:
|
||||
return False, f"Error: {e}"
|
||||
|
||||
def validate_submissions(self) -> dict:
|
||||
"""Validate submissions table."""
|
||||
print("Validating submissions table...")
|
||||
|
||||
db = self.SessionLocal()
|
||||
results = {
|
||||
'total': 0,
|
||||
'relative_paths': 0,
|
||||
'accessible_files': 0,
|
||||
'issues': []
|
||||
}
|
||||
|
||||
try:
|
||||
submissions = db.query(Submission).all()
|
||||
results['total'] = len(submissions)
|
||||
|
||||
for submission in submissions:
|
||||
# Check if path is relative
|
||||
if self.is_relative_path(submission.file_path):
|
||||
results['relative_paths'] += 1
|
||||
|
||||
# Check if file is accessible
|
||||
accessible, full_path = self.validate_file_accessibility(submission.file_path)
|
||||
if accessible:
|
||||
results['accessible_files'] += 1
|
||||
else:
|
||||
issue = f"Submission {submission.id}: File not accessible at {full_path}"
|
||||
results['issues'].append(issue)
|
||||
self.issues.append(issue)
|
||||
else:
|
||||
issue = f"Submission {submission.id}: Still has absolute path: {submission.file_path}"
|
||||
results['issues'].append(issue)
|
||||
self.issues.append(issue)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
return results
|
||||
|
||||
def validate_attachments(self) -> dict:
|
||||
"""Validate task_attachments table."""
|
||||
print("Validating task_attachments table...")
|
||||
|
||||
db = self.SessionLocal()
|
||||
results = {
|
||||
'total': 0,
|
||||
'relative_paths': 0,
|
||||
'accessible_files': 0,
|
||||
'issues': []
|
||||
}
|
||||
|
||||
try:
|
||||
attachments = db.query(TaskAttachment).all()
|
||||
results['total'] = len(attachments)
|
||||
|
||||
for attachment in attachments:
|
||||
# Check if path is relative
|
||||
if self.is_relative_path(attachment.file_path):
|
||||
results['relative_paths'] += 1
|
||||
|
||||
# Check if file is accessible
|
||||
accessible, full_path = self.validate_file_accessibility(attachment.file_path)
|
||||
if accessible:
|
||||
results['accessible_files'] += 1
|
||||
else:
|
||||
issue = f"Attachment {attachment.id}: File not accessible at {full_path}"
|
||||
results['issues'].append(issue)
|
||||
self.issues.append(issue)
|
||||
else:
|
||||
issue = f"Attachment {attachment.id}: Still has absolute path: {attachment.file_path}"
|
||||
results['issues'].append(issue)
|
||||
self.issues.append(issue)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
return results
|
||||
|
||||
def validate_projects(self) -> dict:
|
||||
"""Validate projects table."""
|
||||
print("Validating projects table...")
|
||||
|
||||
db = self.SessionLocal()
|
||||
results = {
|
||||
'total': 0,
|
||||
'relative_paths': 0,
|
||||
'accessible_files': 0,
|
||||
'issues': []
|
||||
}
|
||||
|
||||
try:
|
||||
projects = db.query(Project).filter(Project.thumbnail_path.isnot(None)).all()
|
||||
results['total'] = len(projects)
|
||||
|
||||
for project in projects:
|
||||
# Check if path is relative
|
||||
if self.is_relative_path(project.thumbnail_path):
|
||||
results['relative_paths'] += 1
|
||||
|
||||
# Check if file is accessible
|
||||
accessible, full_path = self.validate_file_accessibility(project.thumbnail_path)
|
||||
if accessible:
|
||||
results['accessible_files'] += 1
|
||||
else:
|
||||
issue = f"Project {project.id}: Thumbnail not accessible at {full_path}"
|
||||
results['issues'].append(issue)
|
||||
self.issues.append(issue)
|
||||
else:
|
||||
issue = f"Project {project.id}: Still has absolute thumbnail path: {project.thumbnail_path}"
|
||||
results['issues'].append(issue)
|
||||
self.issues.append(issue)
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
return results
|
||||
|
||||
def validate_path_format_consistency(self) -> dict:
|
||||
"""Validate that all relative paths follow consistent format."""
|
||||
print("Validating path format consistency...")
|
||||
|
||||
db = self.SessionLocal()
|
||||
results = {
|
||||
'consistent_format': 0,
|
||||
'inconsistent_format': 0,
|
||||
'issues': []
|
||||
}
|
||||
|
||||
try:
|
||||
# Expected format: uploads/[type]/[id]/[filename]
|
||||
|
||||
# Check submissions
|
||||
submissions = db.query(Submission).all()
|
||||
for submission in submissions:
|
||||
if submission.file_path.startswith('uploads/submissions/'):
|
||||
results['consistent_format'] += 1
|
||||
else:
|
||||
issue = f"Submission {submission.id}: Inconsistent path format: {submission.file_path}"
|
||||
results['issues'].append(issue)
|
||||
results['inconsistent_format'] += 1
|
||||
|
||||
# Check attachments
|
||||
attachments = db.query(TaskAttachment).all()
|
||||
for attachment in attachments:
|
||||
if attachment.file_path.startswith('uploads/attachments/'):
|
||||
results['consistent_format'] += 1
|
||||
else:
|
||||
issue = f"Attachment {attachment.id}: Inconsistent path format: {attachment.file_path}"
|
||||
results['issues'].append(issue)
|
||||
results['inconsistent_format'] += 1
|
||||
|
||||
# Check projects
|
||||
projects = db.query(Project).filter(Project.thumbnail_path.isnot(None)).all()
|
||||
for project in projects:
|
||||
if project.thumbnail_path.startswith('uploads/project_thumbnails/'):
|
||||
results['consistent_format'] += 1
|
||||
else:
|
||||
issue = f"Project {project.id}: Inconsistent thumbnail path format: {project.thumbnail_path}"
|
||||
results['issues'].append(issue)
|
||||
results['inconsistent_format'] += 1
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
return results
|
||||
|
||||
def run_validation(self) -> bool:
|
||||
"""Run complete validation and return success status."""
|
||||
print("=" * 60)
|
||||
print("MIGRATION VALIDATION REPORT")
|
||||
print("=" * 60)
|
||||
|
||||
# Validate each table
|
||||
submissions_results = self.validate_submissions()
|
||||
attachments_results = self.validate_attachments()
|
||||
projects_results = self.validate_projects()
|
||||
format_results = self.validate_path_format_consistency()
|
||||
|
||||
# Print results
|
||||
print("\nSUBMISSIONS:")
|
||||
print(f" Total: {submissions_results['total']}")
|
||||
print(f" Relative paths: {submissions_results['relative_paths']}")
|
||||
print(f" Accessible files: {submissions_results['accessible_files']}")
|
||||
print(f" Issues: {len(submissions_results['issues'])}")
|
||||
|
||||
print("\nATTACHMENTS:")
|
||||
print(f" Total: {attachments_results['total']}")
|
||||
print(f" Relative paths: {attachments_results['relative_paths']}")
|
||||
print(f" Accessible files: {attachments_results['accessible_files']}")
|
||||
print(f" Issues: {len(attachments_results['issues'])}")
|
||||
|
||||
print("\nPROJECTS:")
|
||||
print(f" Total: {projects_results['total']}")
|
||||
print(f" Relative paths: {projects_results['relative_paths']}")
|
||||
print(f" Accessible files: {projects_results['accessible_files']}")
|
||||
print(f" Issues: {len(projects_results['issues'])}")
|
||||
|
||||
print("\nPATH FORMAT:")
|
||||
print(f" Consistent format: {format_results['consistent_format']}")
|
||||
print(f" Inconsistent format: {format_results['inconsistent_format']}")
|
||||
|
||||
# Print issues if any
|
||||
if self.issues:
|
||||
print("\nISSUES FOUND:")
|
||||
for issue in self.issues:
|
||||
print(f" - {issue}")
|
||||
|
||||
# Determine overall success
|
||||
total_issues = len(self.issues) + format_results['inconsistent_format']
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
if total_issues == 0:
|
||||
print("✅ VALIDATION PASSED: All file paths are relative and accessible")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ VALIDATION FAILED: {total_issues} issues found")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Main validation function."""
|
||||
validator = MigrationValidator()
|
||||
success = validator.run_validation()
|
||||
|
||||
if success:
|
||||
print("\nMigration validation completed successfully!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\nMigration validation found issues that need attention.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user