from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models class Resource(models.Model): DIFFICULTY_CHOICES = [ ('BEGINNER', 'Beginner'), ('INTERMEDIATE', 'Intermediate'), ('ADVANCED', 'Advanced'), ] TYPE_CHOICES = [ ('VIDEO', 'Video'), ('COURSE', 'Course'), ('ARTICLE', 'Article'), ('DOCS', 'Docs'), ] title = models.CharField(max_length=300) provider = models.CharField(max_length=100) url = models.URLField(max_length=500, unique=True) difficulty_level = models.CharField(max_length=20, choices=DIFFICULTY_CHOICES) duration = models.IntegerField( help_text='Total duration in minutes.', validators=[MinValueValidator(0)], ) type = models.CharField(max_length=20, choices=TYPE_CHOICES) rating = models.FloatField( default=3.5, validators=[MinValueValidator(0.0), MaxValueValidator(5.0)], help_text='Curator-assigned 0.0-5.0 rating used by recommendation ranking.', ) skills = models.ManyToManyField( 'skills.Skill', through='SkillResource', related_name='resources', ) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['title'] def __str__(self): return f'{self.title} ({self.provider})' class SkillResource(models.Model): skill = models.ForeignKey('skills.Skill', on_delete=models.CASCADE) resource = models.ForeignKey(Resource, on_delete=models.CASCADE) relevance_score = models.FloatField( default=1.0, validators=[MinValueValidator(0.0), MaxValueValidator(1.0)], help_text='0.0-1.0 how relevant this resource is to the skill.', ) class Meta: unique_together = [['skill', 'resource']] def __str__(self): return f'{self.skill.skill_name} - {self.resource.title} ({self.relevance_score:.2f})' class ResourceCheckpoint(models.Model): SOURCE_CHOICES = [ ('manual', 'Manual'), ('jsonld', 'schema.org JSON-LD'), ('html', 'HTML parse'), ('youtube_api', 'YouTube API'), ('playwright', 'Playwright'), ('fcc_github', 'freeCodeCamp GitHub'), ] resource = models.ForeignKey( Resource, on_delete=models.CASCADE, related_name='checkpoints', ) order_index = models.IntegerField( validators=[MinValueValidator(1)], help_text='1-based position within the resource.', ) title = models.CharField(max_length=500) url_fragment = models.CharField( max_length=500, blank=True, default='', help_text='Optional anchor/path suffix linking to this module.', ) estimated_minutes = models.IntegerField(null=True, blank=True) source = models.CharField(max_length=20, choices=SOURCE_CHOICES, default='manual') extracted_at = models.DateTimeField(null=True, blank=True) class Meta: unique_together = [['resource', 'order_index']] ordering = ['resource', 'order_index'] def __str__(self): return f'{self.resource.title} #{self.order_index}: {self.title[:60]}'