Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ backup.json
ansible/inventory.yml
education-website-*.json
*.sql
.env
34 changes: 34 additions & 0 deletions web/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
ForumReply,
ForumTopic,
Goods,
Infographic,
LearningStreak,
LessonSummary,
MembershipPlan,
MembershipSubscriptionEvent,
Notification,
Expand Down Expand Up @@ -889,3 +891,35 @@ class VideoRequestAdmin(admin.ModelAdmin):
list_display = ("title", "status", "category", "requester", "created_at")
list_filter = ("status", "category")
search_fields = ("title", "description", "requester__username")


@admin.register(Infographic)
class InfographicAdmin(admin.ModelAdmin):
list_display = ("title", "category", "created_by", "is_published", "views", "shares", "created_at")
list_filter = ("category", "is_published", "subject")
search_fields = ("title", "content", "created_by__username")
raw_id_fields = ("created_by", "subject")
readonly_fields = ("views", "shares", "created_at", "updated_at")
fieldsets = (
(None, {"fields": ("title", "content", "category", "subject", "created_by")}),
("Design", {"fields": ("image", "background_color", "text_color")}),
("Publishing", {"fields": ("is_published",)}),
("Stats", {"fields": ("views", "shares"), "classes": ("collapse",)}),
("Timestamps", {"fields": ("created_at", "updated_at"), "classes": ("collapse",)}),
)


@admin.register(LessonSummary)
class LessonSummaryAdmin(admin.ModelAdmin):
list_display = ("title", "user", "course", "date", "is_public", "created_at")
list_filter = ("is_public", "background_style", "date")
search_fields = ("title", "key_learnings", "user__username")
raw_id_fields = ("user", "course", "session")
readonly_fields = ("created_at", "updated_at")
date_hierarchy = "date"
fieldsets = (
(None, {"fields": ("title", "user", "course", "session")}),
("Content", {"fields": ("key_learnings", "additional_notes")}),
("Settings", {"fields": ("date", "background_style", "is_public")}),
("Timestamps", {"fields": ("created_at", "updated_at"), "classes": ("collapse",)}),
)
75 changes: 74 additions & 1 deletion web/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.validators import FileExtensionValidator
from django.db import IntegrityError
from django.db import IntegrityError, models
from django.forms.widgets import URLInput
from django.utils import timezone
from django.utils.crypto import get_random_string
Expand All @@ -31,6 +31,8 @@
ForumCategory,
Goods,
GradeableLink,
Infographic,
LessonSummary,
LinkGrade,
Meme,
NotificationPreference,
Expand Down Expand Up @@ -110,6 +112,8 @@
"LinkGradeForm",
"AwardAchievementForm",
"SurveyForm",
"InfographicForm",
"LessonSummaryForm",
"VirtualClassroomForm",
"VirtualClassroomCustomizationForm",
]
Expand Down Expand Up @@ -1977,6 +1981,75 @@ def clean_title(self) -> str:
return title


class InfographicForm(forms.ModelForm):
"""Form for creating educational infographics with tips and facts."""

class Meta:
model = Infographic
fields = ["title", "content", "category", "subject", "image", "background_color", "text_color"]
widgets = {
"title": TailwindInput(attrs={"placeholder": "Enter infographic title"}),
"content": TailwindTextarea(attrs={"rows": 5, "placeholder": "Enter your educational tip or fact here..."}),
"category": TailwindSelect(),
"subject": TailwindSelect(),
"image": TailwindFileInput(),
"background_color": forms.TextInput(
attrs={
"type": "color",
"class": "w-full h-10 border border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer",
}
),
"text_color": forms.TextInput(
attrs={
"type": "color",
"class": "w-full h-10 border border-gray-300 dark:border-gray-600 rounded-lg cursor-pointer",
}
),
}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["subject"].required = False
self.fields["image"].required = False


class LessonSummaryForm(forms.ModelForm):
"""Form for creating 'What I Learned Today' lesson summaries."""

class Meta:
model = LessonSummary
fields = ["title", "course", "session", "key_learnings", "additional_notes", "background_style", "is_public"]
widgets = {
"title": TailwindInput(attrs={"placeholder": "What did you learn today?"}),
"course": TailwindSelect(),
"session": TailwindSelect(),
"key_learnings": TailwindTextarea(
attrs={
"rows": 6,
"placeholder": "Enter key learnings (one per line):\nβ€’ Learning point 1\nβ€’ Learning point 2",
}
),
"additional_notes": TailwindTextarea(attrs={"rows": 3, "placeholder": "Any additional thoughts or notes?"}),
"background_style": TailwindSelect(),
"is_public": TailwindCheckboxInput(),
}

def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
self.fields["course"].required = False
self.fields["session"].required = False
self.fields["additional_notes"].required = False

# Filter courses and sessions based on user
if user:
self.fields["course"].queryset = Course.objects.filter(
models.Q(enrollments__user=user) | models.Q(teacher=user)
).distinct()
self.fields["session"].queryset = Session.objects.filter(
models.Q(course__enrollments__user=user) | models.Q(course__teacher=user)
).distinct()


class VirtualClassroomForm(forms.ModelForm):
"""Form for creating and editing virtual classrooms."""

Expand Down
182 changes: 182 additions & 0 deletions web/migrations/0063_infographic_lessonsummary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Generated manually for infographics and lesson summaries feature

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):
dependencies = [
("web", "0062_update_waitingroom_for_sessions"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="Infographic",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"title",
models.CharField(help_text="Title of the infographic", max_length=200),
),
("content", models.TextField(help_text="Main content or fact to display")),
(
"category",
models.CharField(
choices=[
("tip", "Educational Tip"),
("fact", "Did You Know?"),
("summary", "Quick Summary"),
("guide", "How-To Guide"),
],
default="tip",
max_length=20,
),
),
(
"image",
models.ImageField(
blank=True,
help_text="Optional background image",
upload_to="infographics/",
),
),
(
"background_color",
models.CharField(
default="#5EEAD4",
help_text="Hex color for background (e.g., #5EEAD4 for teal-300)",
max_length=7,
),
),
(
"text_color",
models.CharField(
default="#1F2937",
help_text="Hex color for text (e.g., #1F2937)",
max_length=7,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("is_published", models.BooleanField(default=True)),
("views", models.IntegerField(default=0)),
("shares", models.IntegerField(default=0)),
(
"created_by",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="infographics",
to=settings.AUTH_USER_MODEL,
),
),
(
"subject",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="infographics",
to="web.subject",
),
),
],
options={
"verbose_name": "Infographic",
"verbose_name_plural": "Infographics",
"ordering": ["-created_at"],
},
),
migrations.CreateModel(
name="LessonSummary",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"title",
models.CharField(help_text="Title of the lesson summary", max_length=200),
),
(
"key_learnings",
models.TextField(help_text="Main points learned (one per line)"),
),
(
"additional_notes",
models.TextField(blank=True, help_text="Additional notes or thoughts"),
),
("date", models.DateField(default=django.utils.timezone.now)),
(
"background_style",
models.CharField(
choices=[
("gradient1", "Teal Gradient"),
("gradient2", "Blue Gradient"),
("gradient3", "Purple Gradient"),
("solid", "Solid Color"),
],
default="gradient1",
max_length=20,
),
),
(
"is_public",
models.BooleanField(
default=False,
help_text="Make this summary public for others to see",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"course",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="lesson_summaries",
to="web.course",
),
),
(
"session",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="lesson_summaries",
to="web.session",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="lesson_summaries",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "Lesson Summary",
"verbose_name_plural": "Lesson Summaries",
"ordering": ["-date", "-created_at"],
},
),
]
Loading