Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLTV Education section #3684

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a1d7168
Backend structure, dashboard and create/update course
SahilDhillon21 Feb 21, 2025
f253b3b
Course creation/modification UI with view
SahilDhillon21 Feb 21, 2025
8db380c
UI improvements
SahilDhillon21 Feb 21, 2025
39abc49
Merge branch 'main' into bltv-education
DonnieBLT Feb 22, 2025
b778cef
View course setup and ui enhancements
SahilDhillon21 Feb 22, 2025
f646ff4
Course content view and UI
SahilDhillon21 Feb 22, 2025
3412780
Merge branch 'bltv-education' of https://github.com/SahilDhillon21/BL…
SahilDhillon21 Feb 22, 2025
d221363
Finalise course view UI
SahilDhillon21 Feb 23, 2025
0985277
CMS setup
SahilDhillon21 Feb 26, 2025
5469fe6
Description added for sections
SahilDhillon21 Feb 26, 2025
8e514b5
Custom decorator for security and messages. Delete button working
SahilDhillon21 Feb 26, 2025
a15b0fc
Small adjustments
SahilDhillon21 Feb 26, 2025
37d5af3
Study course and final changes
SahilDhillon21 Feb 27, 2025
24517f1
Merge branch 'main' into bltv-education
SahilDhillon21 Feb 27, 2025
661cc83
Merge branch 'bltv-education' of https://github.com/SahilDhillon21/BL…
SahilDhillon21 Feb 27, 2025
fdb0246
Fix security vuln
SahilDhillon21 Feb 27, 2025
6220ab3
Fix migrations
SahilDhillon21 Feb 27, 2025
4f241d8
Remove bacon view to pass pre commit
SahilDhillon21 Feb 27, 2025
7158c64
Improve URL matching in embed_url for better security
SahilDhillon21 Feb 28, 2025
29c648f
Enhanced embed function
SahilDhillon21 Feb 28, 2025
d3ab398
Add bacon view back
SahilDhillon21 Feb 28, 2025
9c9e3c7
Add back comment migrations
SahilDhillon21 Feb 28, 2025
152a723
Merge branch 'main' into bltv-education
SahilDhillon21 Feb 28, 2025
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
55 changes: 54 additions & 1 deletion blt/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,28 @@
update_submission_status,
)
from website.views.blog import PostCreateView, PostDeleteView, PostDetailView, PostListView, PostUpdateView
from website.views.bltv_education import (
add_lecture,
add_section,
bltv_home,
course_content_management,
create_or_update_course,
delete_lecture,
delete_section,
edit_course,
edit_lecture,
edit_section,
enroll,
get_course_content,
get_lecture_data,
get_section_data,
instructor_dashboard,
mark_lecture_complete,
study_course,
update_lectures_order,
update_sections_order,
view_course,
)
from website.views.company import (
AddDomainView,
AddHuntView,
Expand Down Expand Up @@ -599,7 +621,38 @@
name="deletions",
),
re_path(r"^bacon/$", bacon_view, name="bacon"),
re_path(r"^bltv/$", TemplateView.as_view(template_name="bltv.html"), name="bltv"),
re_path(r"^bltv/$", bltv_home, name="bltv"),
path("bltv/instructor_dashboard/", instructor_dashboard, name="instructor_dashboard"),
path("bltv/instructor_dashboard/edit-course/<int:course_id>/", edit_course, name="edit_course"),
path("bltv/instructor_dashboard/create-or-update-course/", create_or_update_course, name="create_or_update_course"),
path("bltv/view-course/<int:course_id>/", view_course, name="view_course"),
path("bltv/enroll/<int:course_id>/", enroll, name="enroll"),
path("bltv/study_course/<int:course_id>/", study_course, name="study_course"),
path("bltv/mark-lecture-complete/", mark_lecture_complete, name="mark_lecture_complete"),
path("bltv/get-course-content/<int:course_id>/", get_course_content, name="get_course_content"),
path(
"bltv/course-content-management/<int:course_id>/", course_content_management, name="course_content_management"
),
path("bltv/instructor_dashboard/courses/<int:course_id>/sections/add/", add_section, name="add_section"),
path("bltv/instructor_dashboard/sections/<int:section_id>/edit/", edit_section, name="edit_section"),
path("bltv/instructor_dashboard/sections/<int:section_id>/delete/", delete_section, name="delete_section"),
# Lecture management
path("bltv/instructor_dashboard/sections/<int:section_id>/lectures/add/", add_lecture, name="add_lecture"),
path("bltv/instructor_dashboard/lectures/<int:lecture_id>/edit/", edit_lecture, name="edit_lecture"),
path("bltv/instructor_dashboard/lectures/<int:lecture_id>/delete/", delete_lecture, name="delete_lecture"),
# API endpoints
path("bltv/instructor_dashboard/api/lectures/<int:lecture_id>/", get_lecture_data, name="get_lecture_data"),
path("bltv/instructor_dashboard/api/sections/<int:section_id>/", get_section_data, name="get_section_data"),
path(
"bltv/instructor_dashboard/courses/<int:course_id>/sections/reorder/",
update_sections_order,
name="update_sections_order",
),
path(
"bltv/instructor_dashboard/sections/<int:section_id>/lectures/reorder/",
update_lectures_order,
name="update_lectures_order",
),
re_path(r"^gsoc/$", TemplateView.as_view(template_name="gsoc.html"), name="gsoc"),
re_path(
r"^privacypolicy/$",
Expand Down
12 changes: 12 additions & 0 deletions website/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
Contribution,
Contributor,
ContributorStats,
Course,
Domain,
Enrollment,
ForumCategory,
ForumComment,
ForumPost,
Expand All @@ -31,6 +33,8 @@
InviteFriend,
Issue,
IssueScreenshot,
Lecture,
LectureStatus,
Message,
Monitor,
Organization,
Expand All @@ -41,8 +45,10 @@
Post,
PRAnalysisReport,
Project,
Rating,
Repo,
Room,
Section,
SlackBotActivity,
SlackIntegration,
Subscription,
Expand Down Expand Up @@ -632,6 +638,12 @@ class RoomAdmin(admin.ModelAdmin):
admin.site.register(Trademark)
admin.site.register(TrademarkOwner)
admin.site.register(OsshCommunity)
admin.site.register(Lecture)
admin.site.register(LectureStatus)
admin.site.register(Course)
admin.site.register(Section)
admin.site.register(Enrollment)
admin.site.register(Rating)
admin.site.register(GitHubIssue, GitHubIssueAdmin)
admin.site.register(GitHubReview, GitHubReviewAdmin)
admin.site.register(Message, MessageAdmin)
Expand Down
28 changes: 28 additions & 0 deletions website/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from functools import wraps

from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404

from website.models import Course, Lecture, Section


def instructor_required(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
course = None

if "course_id" in kwargs:
course = get_object_or_404(Course, id=kwargs["course_id"])
elif "lecture_id" in kwargs:
lecture = get_object_or_404(Lecture, id=kwargs["lecture_id"])
course = lecture.section.course
elif "section_id" in kwargs:
section = get_object_or_404(Section, id=kwargs["section_id"])
course = section.course

if not course or request.user != course.instructor.user:
raise PermissionDenied

return view_func(request, *args, **kwargs)

return _wrapped_view
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Generated by Django 5.1.4 on 2025-02-27 23:53

import django.core.validators
import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("website", "0210_merge_0209_baconsubmission_0209_repo_organization"),
]

operations = [
migrations.CreateModel(
name="Course",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("title", models.CharField(max_length=200)),
("description", models.TextField()),
("thumbnail", models.ImageField(blank=True, null=True, upload_to="course_thumbnails/")),
(
"level",
models.CharField(
choices=[("BEG", "Beginner"), ("INT", "Intermediate"), ("ADV", "Advanced")],
default="BEG",
max_length=3,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"instructor",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="courses_teaching",
to="website.userprofile",
),
),
("tags", models.ManyToManyField(blank=True, related_name="courses", to="website.tag")),
],
),
migrations.CreateModel(
name="Lecture",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("title", models.CharField(max_length=200)),
("description", models.TextField(blank=True, null=True)),
(
"content_type",
models.CharField(
choices=[
("VIDEO", "Video Lecture"),
("LIVE", "Live Session"),
("DOCUMENT", "Document"),
("QUIZ", "Quiz"),
],
max_length=10,
),
),
("video_url", models.URLField(blank=True, null=True)),
("live_url", models.URLField(blank=True, null=True)),
("scheduled_time", models.DateTimeField(blank=True, null=True)),
("recording_url", models.URLField(blank=True, null=True)),
("content", models.TextField()),
("duration", models.PositiveIntegerField(blank=True, help_text="Duration in minutes", null=True)),
("order", models.PositiveIntegerField()),
("tags", models.ManyToManyField(blank=True, related_name="lectures", to="website.tag")),
],
options={
"ordering": ["order"],
},
),
migrations.CreateModel(
name="LectureStatus",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
(
"status",
models.CharField(choices=[("PROGRESS", "In Progress"), ("COMPLETED", "Completed")], max_length=15),
),
(
"lecture",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="lecture_statuses",
to="website.lecture",
),
),
(
"student",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name="student", to="website.userprofile"
),
),
],
),
migrations.CreateModel(
name="Rating",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
(
"score",
models.DecimalField(
decimal_places=2,
max_digits=3,
validators=[
django.core.validators.MinValueValidator(0.0),
django.core.validators.MaxValueValidator(5.0),
],
),
),
("comment", models.TextField(blank=True, null=True)),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"course",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name="ratings", to="website.course"
),
),
("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="website.userprofile")),
],
),
migrations.CreateModel(
name="Section",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("title", models.CharField(max_length=200)),
("description", models.TextField(blank=True, null=True)),
("order", models.PositiveIntegerField()),
(
"course",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name="sections", to="website.course"
),
),
],
options={
"ordering": ["order"],
},
),
migrations.AddField(
model_name="lecture",
name="section",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="lectures",
to="website.section",
),
),
migrations.CreateModel(
name="Enrollment",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("enrolled_at", models.DateTimeField(auto_now_add=True)),
("completed", models.BooleanField(default=False)),
("last_accessed", models.DateTimeField(auto_now=True)),
(
"course",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name="enrollments", to="website.course"
),
),
(
"student",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="enrollments",
to="website.userprofile",
),
),
],
options={
"unique_together": {("student", "course")},
},
),
]
Loading