Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
69aba72
waiting rooms formation
ishaan-arora-1 Mar 20, 2025
d66221b
removed earlier added files
ishaan-arora-1 Mar 20, 2025
c7afa20
added all features of waiting room
ishaan-arora-1 Mar 22, 2025
99c771e
merging upstream
ishaan-arora-1 Mar 22, 2025
cc19f3c
merged migrations
ishaan-arora-1 Mar 22, 2025
2c032d6
switched email verification back to mandatory
ishaan-arora-1 Mar 22, 2025
ca8907d
coderabbit suggestions implemented
ishaan-arora-1 Mar 22, 2025
98efdfb
Merge branch 'main' into waitingroom
ishaan-arora-1 Mar 22, 2025
814dd43
Update and rename 0033_waitingroom_fulfilled_course.py to 0034_waitin…
ishaan-arora-1 Mar 22, 2025
e5e3938
upgraded migrations
ishaan-arora-1 Mar 22, 2025
9187f40
Merge branch 'main' into waitingroom
A1L13N Mar 23, 2025
130de3a
Merge branch 'main' into waitingroom
A1L13N Mar 23, 2025
022f547
Merge branch 'main' into waitingroom
A1L13N Mar 23, 2025
9776d61
Merge branch 'main' into waitingroom
A1L13N Mar 23, 2025
7c58c7f
Merge branch 'main' into waitingroom
ishaan-arora-1 Mar 23, 2025
a4cf478
updated migrations
ishaan-arora-1 Mar 23, 2025
b489478
added proper verification
ishaan-arora-1 Mar 23, 2025
211f9bc
integrating with learn form
ishaan-arora-1 Mar 24, 2025
3aa5bea
M
ishaan-arora-1 Mar 24, 2025
e87e2df
integrated learn form with waiting room
ishaan-arora-1 Mar 24, 2025
709a4e8
Merge remote-tracking branch 'upstream/main' into waitingroom
ishaan-arora-1 Mar 24, 2025
39cd8e8
updated branch and combined migrations
ishaan-arora-1 Mar 24, 2025
3fabcb3
lint
ishaan-arora-1 Mar 24, 2025
542c993
renamed migration
ishaan-arora-1 Mar 24, 2025
d1a33d6
minor rabbit ai changes
ishaan-arora-1 Mar 24, 2025
cc6b250
removed all appearances of waiting room form
ishaan-arora-1 Mar 24, 2025
64047cc
rabbit ai changes minor
ishaan-arora-1 Mar 24, 2025
5b8002b
errors
ishaan-arora-1 Mar 24, 2025
dd19bcf
lintt
ishaan-arora-1 Mar 24, 2025
5d8c776
removed middleware
ishaan-arora-1 Mar 25, 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
83 changes: 50 additions & 33 deletions web/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
SuccessStory,
TeamGoal,
TeamInvite,
WaitingRoom,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)
from .referrals import handle_referral
from .widgets import (
Expand Down Expand Up @@ -350,6 +351,22 @@ def clean_max_students(self):
raise forms.ValidationError(msg)
return max_students

def clean_title(self):
title = self.cleaned_data.get("title")
if not title:
raise forms.ValidationError("Title is required")

# Check if title contains valid characters for slugification
if not re.match(r"^[\w\s-]+$", title):
raise forms.ValidationError("Title can only contain letters, numbers, spaces, and hyphens")

# Check if a course with this slug already exists
slug = slugify(title)
if Course.objects.filter(slug=slug).exists():
raise forms.ValidationError("A course with a similar title already exists.")

return title


class CourseForm(forms.ModelForm):
description = MarkdownxFormField(
Expand Down Expand Up @@ -770,39 +787,39 @@ class Meta:
}


class LearnForm(forms.Form):
subject = forms.CharField(
max_length=100,
widget=TailwindInput(
attrs={
"placeholder": "What would you like to learn?",
"class": "block w-full border rounded p-2 focus:outline-none focus:ring-2 focus:ring-orange-500",
}
),
)
email = forms.EmailField(
widget=TailwindEmailInput(
attrs={
"placeholder": "Your email address",
"class": "block w-full border rounded p-2 focus:outline-none focus:ring-2 focus:ring-orange-500",
}
)
)
message = forms.CharField(
widget=TailwindTextarea(
attrs={
"placeholder": "Tell us more about what you want to learn...",
"rows": 4,
"class": "block w-full border rounded p-2 focus:outline-none focus:ring-2 focus:ring-orange-500",
}
),
required=False,
)
captcha = CaptchaField(
widget=TailwindCaptchaTextInput(
attrs={"class": "block w-full border rounded p-2 focus:outline-none focus:ring-2 focus:ring-orange-500"}
)
)
class LearnForm(forms.ModelForm):
"""Form for creating and editing waiting rooms."""

class Meta:
model = WaitingRoom
fields = ["title", "description", "subject", "topics"]

widgets = {
"title": TailwindInput(attrs={"placeholder": "What would you like to learn?"}),
"description": TailwindTextarea(attrs={"rows": 4, "placeholder": "Describe what you want to learn"}),
"subject": TailwindInput(attrs={"placeholder": "Main subject (e.g., Mathematics, Programming)"}),
"topics": TailwindInput(
attrs={"placeholder": "e.g., Python, Machine Learning, Data Science", "class": "tag-input"}
),
}
help_texts = {
"title": "Give your waiting room a descriptive title",
"subject": "The main subject area for this waiting room",
"topics": "Enter topics separated by commas",
}

def clean_topics(self):
"""Validate and clean the topics field."""
topics = self.cleaned_data.get("topics")
if not topics:
raise forms.ValidationError("Please enter at least one topic.")

# Ensure we have at least one non-empty topic after splitting
topic_list = [t.strip() for t in topics.split(",") if t.strip()]
if not topic_list:
raise forms.ValidationError("Please enter at least one valid topic.")

return topics

Comment thread
ishaan-arora-1 marked this conversation as resolved.

class TeachForm(forms.Form):
Expand Down
63 changes: 63 additions & 0 deletions web/migrations/0044_waitingroom_fulfilled_course.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Generated by Django 5.1.6 on 2025-03-24 07:12

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


class Migration(migrations.Migration):

dependencies = [
("web", "0043_alter_studygroup_members_studygroupinvite"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="WaitingRoom",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("title", models.CharField(max_length=200)),
("description", models.TextField(blank=True)),
("subject", models.CharField(max_length=100)),
("topics", models.TextField(help_text="Comma-separated list of topics")),
(
"status",
models.CharField(
choices=[("open", "Open"), ("closed", "Closed"), ("fulfilled", "Fulfilled")],
default="open",
max_length=10,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"creator",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="created_waiting_rooms",
to=settings.AUTH_USER_MODEL,
),
),
(
"participants",
models.ManyToManyField(
blank=True,
related_name="joined_waiting_rooms",
to=settings.AUTH_USER_MODEL,
),
),
(
"fulfilled_course",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="fulfilled_waiting_rooms",
to="web.course",
),
),
],
options={},
),
]
40 changes: 40 additions & 0 deletions web/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2150,6 +2150,28 @@ def created_at(self):
return self.start_time


class WaitingRoom(models.Model):
"""Model for storing waiting room requests for courses on specific subjects."""

STATUS_CHOICES = [("open", "Open"), ("closed", "Closed"), ("fulfilled", "Fulfilled")]

title = models.CharField(max_length=200)
description = models.TextField(blank=True)
subject = models.CharField(max_length=100)
Comment thread
ishaan-arora-1 marked this conversation as resolved.
topics = models.TextField(help_text="Comma-separated list of topics")
creator = models.ForeignKey(User, on_delete=models.CASCADE, related_name="created_waiting_rooms")
participants = models.ManyToManyField(User, related_name="joined_waiting_rooms", blank=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default="open")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
fulfilled_course = models.ForeignKey(
"Course", on_delete=models.SET_NULL, null=True, blank=True, related_name="fulfilled_waiting_rooms"
)
Comment thread
ishaan-arora-1 marked this conversation as resolved.

def __str__(self):
return self.title


class GradeableLink(models.Model):
"""Model for storing links that users want to get grades on."""

Expand All @@ -2175,6 +2197,24 @@ class Meta:
def __str__(self):
return self.title

def participant_count(self):
"""Return the number of participants in the waiting room."""
return self.participants.count()

def topic_list(self):
"""Return the list of topics as a list."""
return [topic.strip() for topic in self.topics.split(",") if topic.strip()]

def mark_as_fulfilled(self, course=None):
"""Mark the waiting room as fulfilled and notify participants."""
self.status = "fulfilled"
self.save()

if course:
from .notifications import notify_waiting_room_fulfilled

notify_waiting_room_fulfilled(self, course)

def get_absolute_url(self):
return reverse("gradeable_link_detail", kwargs={"pk": self.pk})

Expand Down
52 changes: 52 additions & 0 deletions web/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,58 @@ def send_weekly_progress_updates():
)


def notify_waiting_room_fulfilled(waiting_room, course):
"""
Notify all participants in a waiting room that a course has been created.

Args:
waiting_room (WaitingRoom): The waiting room that was fulfilled
course (Course): The course that was created from the waiting room
"""
subject = f"New Course Created: {course.title}"

# Notify all participants
for participant in waiting_room.participants.all():
notification_data = {
"title": subject,
"message": f"A new course has been created based on a waiting room you joined: '{waiting_room.title}'. "
f"The course '{course.title}' is now available for enrollment.",
"notification_type": "success",
}

# Send notification
send_notification(participant, notification_data)

# Send email with more details
html_message = render_to_string(
"emails/waiting_room_fulfilled.html",
{
"user": participant,
"waiting_room": waiting_room,
"course": course,
"site_url": settings.SITE_URL,
},
)

send_mail(
subject,
"", # Plain text version - we're only sending HTML
settings.DEFAULT_FROM_EMAIL,
[participant.email],
html_message=html_message,
)

# Also notify the creator if they're not already a participant
if waiting_room.creator not in waiting_room.participants.all():
notification_data = {
"title": subject,
"message": f"A new course has been created based on your waiting room: '{waiting_room.title}'. "
f"The course '{course.title}' is now available.",
"notification_type": "success",
}
send_notification(waiting_room.creator, notification_data)


def send_email(subject, message, recipient_list):
"""
Send an email to the specified recipients and notify Slack.
Expand Down
14 changes: 14 additions & 0 deletions web/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@
</button>
<div class="absolute left-0 mt-1 w-56 bg-white dark:bg-gray-800 rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50 transform origin-top-left">
<div class="py-2 px-1">
<a href="{% url 'waiting_rooms' %}"
class="block px-4 py-2 rounded-md text-gray-700 dark:text-gray-200 hover:bg-teal-50 dark:hover:bg-teal-900 hover:text-teal-600 dark:hover:text-teal-300">
<i class="fas fa-chalkboard-teacher mr-2 text-teal-500"></i>Learning Requests
</a>
<a href="{% url 'forum_categories' %}"
class="block px-4 py-2 rounded-md text-gray-700 dark:text-gray-200 hover:bg-teal-50 dark:hover:bg-teal-900 hover:text-teal-600 dark:hover:text-teal-300">
<i class="fas fa-comments mr-2 text-teal-500"></i>Forum
Expand Down Expand Up @@ -543,6 +547,11 @@
class="fas fa-chevron-down text-gray-500 dark:text-gray-400 transition-transform duration-200"></i>
</div>
<div id="community-accordion" class="hidden mt-2 ml-2 space-y-2">
<a href="{% url 'waiting_rooms' %}"
class="flex items-center py-2 px-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md">
<i class="fas fa-chalkboard-teacher mr-2 text-teal-500"></i>
<span>Learning Requests</span>
</a>
<a href="{% url 'forum_categories' %}"
class="flex items-center py-2 px-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md">
<i class="fas fa-comments mr-2 text-teal-500"></i>
Expand Down Expand Up @@ -573,6 +582,11 @@
<i class="fas fa-users-cog mr-2 text-teal-500"></i>
<span>Teams</span>
</a>
<a href="{% url 'waiting_room_list' %}"
class="flex items-center py-2 px-3 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md">
<i class="fas fa-users-cog mr-2 text-teal-500"></i>
<span>Waiting Rooms</span>
</a>
</div>
</div>
<!-- RESOURCES Section -->
Expand Down
20 changes: 20 additions & 0 deletions web/templates/courses/create.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@
<h1 class="text-3xl font-bold mb-4">Create New Course</h1>
<p class="text-gray-600 dark:text-gray-300">Fill out the form below to create your new course.</p>
</div>
<!-- Conditional Waiting Room Warning Banner -->
{% if request.session.waiting_room_data %}
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-4 mb-6">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-yellow-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<p class="text-sm text-yellow-700">
Creating course for waiting room. Ensure the subject and topics match the waiting room's request.
Comment thread
A1L13N marked this conversation as resolved.
</p>
</div>
</div>
</div>
{% endif %}
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
<form method="post"
enctype="multipart/form-data"
Expand Down
26 changes: 23 additions & 3 deletions web/templates/emails/learn_interest.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,37 @@
</head>
<body>
<div class="header">
<h1>New Learning Interest</h1>
<h1>New Learning Request</h1>
</div>
<div class="content">
<h2>Subject Interest: {{ subject }}</h2>
<h2>{{ title }}</h2>
<p>
<strong>Waiting Room ID:</strong> {{ waiting_room_id }}
</p>
<p>
<strong>Subject:</strong> {{ subject }}
</p>
<p>
<strong>Topics:</strong> {{ topics }}
</p>
<p>
<strong>From:</strong> {{ email }}
</p>
<h3>Description:</h3>
<p>{{ description }}</p>
{% if message %}
<h3>Additional Information:</h3>
<h3>Additional Requirements:</h3>
<p>{{ message }}</p>
{% endif %}
<p class="action-link" style="margin-top: 20px;">
<a href="{% url 'create_course_from_waiting_room' waiting_room_id %}"
style="background-color: #f97316;
color: white;
padding: 10px 20px;
text-decoration: none;
border-radius: 5px;
display: inline-block">Create Course from this Request</a>
</p>
</div>
<div class="footer">
<p>This is an automated message from Alpha One Labs Learning Platform</p>
Expand Down
Loading