File size: 3,171 Bytes
ffd36e0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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]}'