Spaces:
Runtime error
Runtime error
File size: 4,194 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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | from django import forms
from django.contrib import admin, messages
from django.db import transaction
from django.db.models import Max
from .models import Resource, ResourceCheckpoint, SkillResource
class SkillResourceInline(admin.TabularInline):
model = SkillResource
extra = 1
autocomplete_fields = ['skill']
class ResourceCheckpointInline(admin.TabularInline):
model = ResourceCheckpoint
extra = 0
fields = ('order_index', 'title', 'url_fragment', 'estimated_minutes', 'source')
ordering = ('order_index',)
class ResourceAdminForm(forms.ModelForm):
bulk_checkpoints = forms.CharField(
required=False,
widget=forms.Textarea(attrs={'rows': 12, 'cols': 80}),
label='Bulk add checkpoints',
help_text=(
'Paste module/lesson titles, one per line (source=manual). '
'Only applied when this resource has NO checkpoints yet; if it '
'already has any, the paste is ignored — edit the inline rows '
'below instead. Blank lines ignored.'
),
)
class Meta:
model = Resource
exclude = ['skills']
@admin.register(Resource)
class ResourceAdmin(admin.ModelAdmin):
form = ResourceAdminForm
list_display = ('title', 'provider', 'type', 'difficulty_level', 'duration', 'rating')
list_filter = ('provider', 'type', 'difficulty_level')
search_fields = ('title', 'provider', 'url')
inlines = [SkillResourceInline, ResourceCheckpointInline]
fieldsets = (
(None, {
'fields': ('title', 'provider', 'url', 'type', 'difficulty_level',
'duration', 'rating'),
}),
('Bulk checkpoint paste', {
'classes': ('collapse',),
'fields': ('bulk_checkpoints',),
'description': 'Optional shortcut for adding multiple checkpoints at once.',
}),
)
def save_model(self, request, obj, form, change):
bulk = (form.cleaned_data.get('bulk_checkpoints') or '').strip()
lines = [line.strip() for line in bulk.splitlines() if line.strip()] if bulk else []
with transaction.atomic():
super().save_model(request, obj, form, change)
if not lines:
return
# Refuse to append when checkpoints already exist — the admin
# should use the inline to edit existing rows. This prevents
# accidental duplication and silent orphaning of
# UserCheckpointProgress rows when the admin re-edits the list.
if obj.checkpoints.exists():
self.message_user(
request,
"Bulk paste ignored: this resource already has checkpoints. "
"Edit them via the inline below instead.",
level=messages.WARNING,
)
return
current_max = obj.checkpoints.aggregate(m=Max('order_index'))['m'] or 0
ResourceCheckpoint.objects.bulk_create([
ResourceCheckpoint(
resource=obj,
order_index=current_max + offset,
title=line,
source='manual',
)
for offset, line in enumerate(lines, start=1)
])
self.message_user(
request,
f"Added {len(lines)} checkpoint(s) via bulk paste.",
level=messages.SUCCESS,
)
@admin.register(ResourceCheckpoint)
class ResourceCheckpointAdmin(admin.ModelAdmin):
list_display = ('resource', 'order_index', 'title', 'source', 'estimated_minutes')
list_filter = ('source', 'resource__provider')
search_fields = ('title', 'resource__title')
ordering = ('resource', 'order_index')
@admin.register(SkillResource)
class SkillResourceAdmin(admin.ModelAdmin):
list_display = ('skill', 'resource', 'relevance_score')
list_filter = ('skill__category',)
search_fields = ('skill__skill_name', 'resource__title')
autocomplete_fields = ['skill', 'resource']
|