diff --git a/corpus/accounts/migrations/0004_alter_executivemember_date_joined.py b/corpus/accounts/migrations/0004_alter_executivemember_date_joined.py new file mode 100644 index 00000000..e54cc466 --- /dev/null +++ b/corpus/accounts/migrations/0004_alter_executivemember_date_joined.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.4 on 2024-01-24 13:40 +import datetime + +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + + dependencies = [ + ("accounts", "0003_alter_executivemember_date_joined"), + ] + + operations = [ + migrations.AlterField( + model_name="executivemember", + name="date_joined", + field=models.DateTimeField( + default=datetime.datetime(2024, 1, 24, 19, 10, 0, 912845), + verbose_name="Date Joined", + ), + ), + ] diff --git a/corpus/corpus/settings.py b/corpus/corpus/settings.py index 31a7266e..68377e27 100644 --- a/corpus/corpus/settings.py +++ b/corpus/corpus/settings.py @@ -28,7 +28,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = (os.getenv("ENVIRONMENT") == "DEVELOPMENT") or ( - os.getenv("LIVECYCLE") is not None + os.getenv("LIVECYCLE") is not None ) # TODO: Stricter host policies @@ -57,7 +57,8 @@ "embedathon.apps.EmbedathonConfig", "impulse.apps.ImpulseConfig", "electrika.apps.ElectrikaConfig", - "skyward_expedition.apps.SkywardExpeditionConfig" + "skyward_expedition.apps.SkywardExpeditionConfig", + "robotrix.apps.RobotrixConfig", ] MIDDLEWARE = [ diff --git a/corpus/corpus/urls.py b/corpus/corpus/urls.py index 1c0a7784..89d670e4 100644 --- a/corpus/corpus/urls.py +++ b/corpus/corpus/urls.py @@ -26,4 +26,5 @@ path("impulse/", include("impulse.urls")), path("electrika/", include("electrika.urls")), path("skyward_expedition/", include("skyward_expedition.urls")), + path("robotrix/", include("robotrix.urls")), ] diff --git a/corpus/robotrix/__init__.py b/corpus/robotrix/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/corpus/robotrix/admin.py b/corpus/robotrix/admin.py new file mode 100644 index 00000000..808ba0bb --- /dev/null +++ b/corpus/robotrix/admin.py @@ -0,0 +1,12 @@ +from django.contrib import admin +from robotrix.models import Announcement +from robotrix.models import Invite +from robotrix.models import RobotrixUser +from robotrix.models import Team + +# Register your models here. + +admin.site.register(RobotrixUser) +admin.site.register(Team) +admin.site.register(Announcement) +admin.site.register(Invite) diff --git a/corpus/robotrix/apps.py b/corpus/robotrix/apps.py new file mode 100644 index 00000000..9253938c --- /dev/null +++ b/corpus/robotrix/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class RobotrixConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "robotrix" diff --git a/corpus/robotrix/forms.py b/corpus/robotrix/forms.py new file mode 100644 index 00000000..f241105f --- /dev/null +++ b/corpus/robotrix/forms.py @@ -0,0 +1,69 @@ +from django import forms +from robotrix.models import Announcement +from robotrix.models import Invite +from robotrix.models import RobotrixUser +from robotrix.models import Team + +from corpus.forms import CorpusModelForm + + +class RobotrixForm(CorpusModelForm): + class Meta: + model = RobotrixUser + fields = [ + "from_nitk", + "college_name", + "roll_no", + "ieee_member", + "ieee_membership_no", + "phone_no", + ] + + def clean(self): + data = self.cleaned_data + if data.get("from_nitk", None) and not data.get("roll_no", None): + raise forms.ValidationError( + "Enter your roll number for verification that you are from NITK" + ) + + if data.get("ieee_member", None) and not data.get("ieee_membership_no", None): + raise forms.ValidationError( + "Enter your IEEE Membership Number for verification " + + "that you are an IEEE member" + ) + + return data + + +class TeamCreationForm(CorpusModelForm): + class Meta: + model = Team + fields = ["team_name"] + + +class AnnouncementForm(CorpusModelForm): + ANNOUNCEMENT_OPTIONS = [ + ("1", "No email to be sent."), + ("2", "Send email to all team leaders."), + ("3", "Send email to all members"), + ] + + announcement_mailing = forms.ChoiceField( + widget=forms.Select, choices=ANNOUNCEMENT_OPTIONS + ) + + class Meta: + model = Announcement + fields = [ + "content", + "url_link", + "url_link_text", + "announcement_type", + "announcement_mailing", + ] + + +class InviteForm(CorpusModelForm): + class Meta: + model = Invite + fields = ["invite_email"] diff --git a/corpus/robotrix/migrations/0001_initial.py b/corpus/robotrix/migrations/0001_initial.py new file mode 100644 index 00000000..08404e2d --- /dev/null +++ b/corpus/robotrix/migrations/0001_initial.py @@ -0,0 +1,160 @@ +# Generated by Django 4.2.4 on 2024-02-18 14:33 +import django.db.models.deletion +from django.conf import settings +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Announcement", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("content", models.TextField()), + ("url_link", models.URLField(blank=True, null=True)), + ( + "url_link_text", + models.CharField(blank=True, max_length=200, null=True), + ), + ( + "announcement_type", + models.CharField( + choices=[ + ("A", "All Robotrix Users"), + ("P", "Paid Teams"), + ("U", "Unpaid Teams"), + ("N", "Registered for Robotrix but no team"), + ("NI", "Not Registered for Robotrix"), + ], + default="A", + max_length=2, + ), + ), + ("date_created", models.DateTimeField(auto_now_add=True)), + ("date_modified", models.DateTimeField(auto_now=True)), + ], + ), + migrations.CreateModel( + name="RobotrixUser", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("from_nitk", models.BooleanField(default=True)), + ( + "college_name", + models.CharField( + default="National Institute of Technology Karnataka", + max_length=200, + ), + ), + ("roll_no", models.CharField(blank=True, max_length=8, null=True)), + ("ieee_member", models.BooleanField(default=False)), + ("ieee_membership_no", models.BigIntegerField(blank=True, null=True)), + ( + "phone_no", + models.CharField( + max_length=15, unique=True, verbose_name="Phone Number" + ), + ), + ], + ), + migrations.CreateModel( + name="Team", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("team_name", models.CharField(max_length=200)), + ( + "payment_status", + models.CharField( + choices=[ + ("E", "Exempt"), + ("U", "Fee Not Paid"), + ("P", "Fee Paid"), + ], + default="U", + max_length=1, + ), + ), + ( + "team_leader", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="leader", + to="robotrix.robotrixuser", + ), + ), + ], + ), + migrations.AddField( + model_name="robotrixuser", + name="team", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="team", + to="robotrix.team", + ), + ), + migrations.AddField( + model_name="robotrixuser", + name="user", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL + ), + ), + migrations.CreateModel( + name="Invite", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("invite_email", models.EmailField(max_length=254)), + ( + "inviting_team", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="invite_to_team", + to="robotrix.team", + ), + ), + ], + ), + ] diff --git a/corpus/robotrix/migrations/__init__.py b/corpus/robotrix/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/corpus/robotrix/models.py b/corpus/robotrix/models.py new file mode 100644 index 00000000..3c0a2bc9 --- /dev/null +++ b/corpus/robotrix/models.py @@ -0,0 +1,72 @@ +from accounts.models import User +from django.db import models +from embedathon.models import PAYMENT_STATUS + + +class RobotrixUser(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + from_nitk = models.BooleanField(default=True) + college_name = models.CharField( + max_length=200, default="National Institute of Technology Karnataka" + ) + roll_no = models.CharField(max_length=8, blank=True, null=True) + ieee_member = models.BooleanField(default=False) + ieee_membership_no = models.BigIntegerField(blank=True, null=True) + phone_no = models.CharField( + max_length=15, + unique=True, + verbose_name="Phone Number", + ) + team = models.ForeignKey( + "Team", on_delete=models.CASCADE, related_name="team", blank=True, null=True + ) + + def __str__(self): + return self.user.email + + +class Team(models.Model): + team_name = models.CharField(max_length=200, blank=False, null=False) + team_leader = models.ForeignKey( + RobotrixUser, on_delete=models.CASCADE, related_name="leader" + ) + + payment_status = models.CharField( + max_length=1, choices=PAYMENT_STATUS, blank=False, null=False, default="U" + ) + + def __str__(self): + return self.team_name + + +class Announcement(models.Model): + + AnnouncementType = ( + ("A", "All Robotrix Users"), + ("P", "Paid Teams"), + ("U", "Unpaid Teams"), + ("N", "Registered for Robotrix but no team"), + ("NI", "Not Registered for Robotrix"), + ) + + content = models.TextField(blank=False, null=False) + url_link = models.URLField(blank=True, null=True) + url_link_text = models.CharField(max_length=200, blank=True, null=True) + announcement_type = models.CharField( + max_length=2, choices=AnnouncementType, blank=False, null=False, default="A" + ) + date_created = models.DateTimeField(auto_now_add=True) + date_modified = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.content[:20] + "..." + + +class Invite(models.Model): + inviting_team = models.ForeignKey( + Team, on_delete=models.CASCADE, related_name="invite_to_team" + ) + invite_email = models.EmailField(blank=False, null=False) + + def __str__(self): + return self.invite_email diff --git a/corpus/robotrix/urls.py b/corpus/robotrix/urls.py new file mode 100644 index 00000000..2cbc8699 --- /dev/null +++ b/corpus/robotrix/urls.py @@ -0,0 +1,40 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path("", views.home, name="robotrix_home"), + path("index", views.index, name="robotrix_index"), + path("register", views.register, name="robotrix_register"), + path("create_team", views.create_team, name="robotrix_create_team"), + path("create_invite", views.create_invite, name="robotrix_create_invite"), + path("accept_invite/", views.accept_invite, name="robotrix_accept_invite"), + path("delete_invite/", views.delete_invite, name="robotrix_delete_invite"), + path("admin", views.admin, name="robotrix_admin"), + path("admin/teams", views.team_management, name="robotrix_admin_teams"), + path("admin/team/", views.team_page, name="robotrix_admin_team_page"), + path( + "admin/team//mark_payment_complete", + views.mark_payment_complete, + name="robotrix_admin_mark_payment_complete", + ), + path( + "admin/team//mark_payment_incomplete", + views.mark_payment_incomplete, + name="robotrix_admin_mark_payment_incomplete", + ), + path("admin/users", views.user_management, name="robotrix_admin_users"), + path( + "admins/announcements", + views.announcements_management, + name="robotrix_announcements", + ), + path( + "admin/announcements/delete/", + views.delete_announcement, + name="robotrix_delete_announcement", + ), + path( + "admin/team_download", views.team_download, name="robotrix_admin_team_download" + ), +] diff --git a/corpus/robotrix/views.py b/corpus/robotrix/views.py new file mode 100644 index 00000000..6008ac4f --- /dev/null +++ b/corpus/robotrix/views.py @@ -0,0 +1,545 @@ +from datetime import datetime + +from accounts.models import User +from config.models import DATETIME_FORMAT +from config.models import ModuleConfiguration +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.db.models import Count +from django.shortcuts import redirect +from django.shortcuts import render +from robotrix.forms import AnnouncementForm +from robotrix.forms import InviteForm +from robotrix.forms import RobotrixForm +from robotrix.forms import TeamCreationForm +from robotrix.models import Announcement +from robotrix.models import Invite +from robotrix.models import RobotrixUser +from robotrix.models import Team + +from corpus.decorators import ensure_group_membership +from corpus.decorators import module_enabled +from corpus.utils import send_email + + +@module_enabled(module_name="robotrix") +def home(request): + args = {} + # Checking if user is Robotrix_admin group member + if request.user.groups.filter(name="robotrix_admin").exists(): + args = {"admin": True} + config = ModuleConfiguration.objects.get(module_name="robotrix").module_config + + try: + if request.user.is_authenticated: + robotrix_user = RobotrixUser.objects.get(user=request.user) + args["registered"] = True + args["robotrix_user"] = robotrix_user + except RobotrixUser.DoesNotExist: + args["registered"] = False + + reg_start_datetime, reg_end_datetime = ( + config["reg_start_datetime"], + config["reg_end_datetime"], + ) + + reg_start_datetime, reg_end_datetime = datetime.strptime( + reg_start_datetime, DATETIME_FORMAT + ), datetime.strptime(reg_end_datetime, DATETIME_FORMAT) + + registration_active = (reg_start_datetime <= datetime.now()) and ( + datetime.now() <= reg_end_datetime + ) + + registration_done = reg_end_datetime < datetime.now() + + args["registration_active"] = registration_active + args["registration_done"] = registration_done + + return render( + request, + "robotrix/home.html", + args, + ) + + +@login_required +@module_enabled(module_name="robotrix") +def index(request): + args = {} + try: + robotrix_user = RobotrixUser.objects.get(user=request.user) + args["robotrix_user"] = robotrix_user + except RobotrixUser.DoesNotExist: + messages.error(request, "Please register for Robotrix first!") + return redirect("robotrix_register") + + if robotrix_user.team is not None: + args["in_team"] = True + + args["team_creation_form"] = TeamCreationForm(instance=robotrix_user.team) + + team = robotrix_user.team + members = RobotrixUser.objects.filter(team=team) + team_count = members.count() + + config = ModuleConfiguration.objects.get(module_name="robotrix").module_config + + max_count = int(config["max_team_size"]) + + if team_count >= max_count: + args["team_full"] = True + if team.team_leader == robotrix_user: + args["is_leader"] = True + invites = Invite.objects.filter(inviting_team=team) + args["invites_from_team"] = invites + args["invite_form"] = InviteForm() + else: + args["is_leader"] = False + + args["team_count"] = team_count + + args["team"] = team + args["members"] = members + args["payment_status"] = team.payment_status + + pay_status = "Not Registered" + + if args["payment_status"] == "E" or args["payment_status"] == "P": + pay_status = "Complete" + elif args["payment_status"] == "U": + pay_status = "Incomplete" + + else: + args["in_team"] = False + args["is_leader"] = False + args["team_creation_form"] = TeamCreationForm() + invites = Invite.objects.filter(invite_email=robotrix_user.user.email) + args["invites_for_user"] = invites + + config = ModuleConfiguration.objects.get(module_name="robotrix").module_config + + reg_start_datetime, reg_end_datetime = ( + config["reg_start_datetime"], + config["reg_end_datetime"], + ) + + reg_start_datetime, reg_end_datetime = datetime.strptime( + reg_start_datetime, DATETIME_FORMAT + ), datetime.strptime(reg_end_datetime, DATETIME_FORMAT) + + registration_active = (reg_start_datetime <= datetime.now()) and ( + datetime.now() <= reg_end_datetime + ) + + args["registration_active"] = registration_active + + try: + if pay_status == "Complete": + announcements = Announcement.objects.filter( + announcement_type__in=["A", "P"] + ) + elif pay_status == "Incomplete": + announcements = Announcement.objects.filter( + announcement_type__in=["A", "U"] + ) + else: + announcements = Announcement.objects.filter( + announcement_type__in=["A", "N"] + ) + except Exception: + announcements = Announcement.objects.filter(announcement_type__in=["A", "N"]) + + announcements = announcements.order_by("-date_created") + + args["announcements"] = announcements + + return render(request, "robotrix/index.html", args) + + +@login_required +@module_enabled(module_name="robotrix") +def register(request): + config = ModuleConfiguration.objects.get(module_name="robotrix").module_config + + reg_start_datetime, reg_end_datetime = ( + config["reg_start_datetime"], + config["reg_end_datetime"], + ) + + reg_start_datetime, reg_end_datetime = datetime.strptime( + reg_start_datetime, DATETIME_FORMAT + ), datetime.strptime(reg_end_datetime, DATETIME_FORMAT) + + registration_active = (reg_start_datetime <= datetime.now()) and ( + datetime.now() <= reg_end_datetime + ) + + if not registration_active: + messages.error(request, "Registration for Robotrix is not active yet!") + return redirect("index") + + try: + robotrix_user = RobotrixUser.objects.get(user=request.user) + messages.error(request, "You have already registered for Robotrix!") + return redirect("robotrix_index") + except RobotrixUser.DoesNotExist: + pass + + if request.method == "POST": + form = RobotrixForm(request.POST) + if form.is_valid(): + robotrix_user = form.save(commit=False) + robotrix_user.user = request.user + robotrix_user.save() + messages.success(request, "Successfully registered for Robotrix!") + return redirect("robotrix_index") + else: + messages.error(request, "Please correct the errors before registering!") + + else: + form = RobotrixForm() + + args = {"form": form} + return render(request, "robotrix/register.html", args) + + +@login_required +@module_enabled(module_name="robotrix") +def create_team(request): + if request.method == "POST": + form = TeamCreationForm(request.POST) + robotrix_user = RobotrixUser.objects.get(user=request.user) + if robotrix_user.team is not None: + if form.is_valid(): + team = robotrix_user.team + team.team_name = form.cleaned_data["team_name"] + team.save() + messages.success(request, "Successfully updated team name!") + return redirect("robotrix_index") + else: + if form.is_valid(): + team = form.save(commit=False) + robotrix_user = RobotrixUser.objects.get(user=request.user) + team.team_leader = robotrix_user + + if robotrix_user.from_nitk or robotrix_user.ieee_member: + team.payment_status = "E" + else: + team.payment_status = "U" + + team.save() + robotrix_user.team = team + robotrix_user.save() + messages.success(request, "Successfully created team!") + return redirect("robotrix_index") + else: + messages.error(request, "Please correct the errors before creating team!") + return redirect("robotrix_index") + + +@login_required +@module_enabled(module_name="robotrix") +def create_invite(request): + robotrix_user = RobotrixUser.objects.get(user=request.user) + if request.method == "POST": + form = InviteForm(request.POST) + if form.is_valid(): + if request.user.email == form.cleaned_data["invite_email"]: + messages.error(request, "You cannot invite yourself!") + return redirect("robotrix_index") + + try: + user = User.objects.get(email=form.cleaned_data["invite_email"]) + invited_imp_user = RobotrixUser.objects.get(user=user) + if invited_imp_user.team is not None: + messages.error(request, "User is already in a team!") + return redirect("robotrix_index") + except (User.DoesNotExist, RobotrixUser.DoesNotExist): + pass + + try: + invite = Invite.objects.get( + inviting_team=robotrix_user.team, + invite_email=form.cleaned_data["invite_email"], + ) + messages.error(request, "Invite has already been sent!") + return redirect("robotrix_index") + except Invite.DoesNotExist: + pass + + invite_counts = Invite.objects.filter( + inviting_team=robotrix_user.team + ).count() + + team_members = RobotrixUser.objects.filter(team=robotrix_user.team).count() + + config = ModuleConfiguration.objects.get( + module_name="robotrix" + ).module_config + max_count = int(config["max_team_size"]) + + if invite_counts >= max_count or team_members >= max_count: + messages.error(request, "Maximum team member limit reached!") + return redirect("robotrix_index") + + invite = form.save(commit=False) + inviting_team = robotrix_user.team + invite.inviting_team = inviting_team + invite.save() + + messages.success(request, "Invite sent!") + return redirect("robotrix_index") + messages.error(request, "Illegal Request") + return redirect("robotrix_index") + + +@login_required +@module_enabled(module_name="robotrix") +def accept_invite(request, pk): + invite = Invite.objects.get(pk=pk) + team_members = RobotrixUser.objects.filter(team=invite.inviting_team).count() + config = ModuleConfiguration.objects.get(module_name="robotrix").module_config + max_count = int(config["max_team_size"]) + + if team_members >= max_count: + invite.delete() + messages.error(request, "Maximum team member limit reached!") + return redirect("robotrix_index") + + if request.user.email != invite.invite_email: + messages.error(request, "Illegal request") + return redirect("robotrix_index") + + robotrix_user = RobotrixUser.objects.get(user=request.user) + robotrix_user.team = invite.inviting_team + robotrix_user.save() + + if robotrix_user.from_nitk or robotrix_user.ieee_member: + inviting_team = invite.inviting_team + inviting_team.payment_status = "E" + inviting_team.save() + + Invite.objects.filter(invite_email=request.user.email).delete() + + messages.success(request, "Invite accepted!") + return redirect("robotrix_index") + + +@login_required +@module_enabled(module_name="robotrix") +def delete_invite(request, pk): + invite = Invite.objects.get(pk=pk) + invite.delete() + + messages.success(request, "Invite deleted!") + return redirect("robotrix_index") + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def admin(request): + return render(request, "robotrix/admin/admin.html", {}) + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def team_management(request): + args = {} + args["teams"] = Team.objects.all() + return render(request, "robotrix/admin/teams.html", args) + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def team_page(request, pk): + args = {} + team = Team.objects.get(pk=pk) + args["team"] = team + args["members"] = RobotrixUser.objects.filter(team=team) + return render(request, "robotrix/admin/team_page.html", args) + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def user_management(request): + args = {} + users = RobotrixUser.objects.all() + + nitk_count = users.values("from_nitk").annotate(count=Count("from_nitk")) + ieee_count = users.values("ieee_member").annotate(count=Count("ieee_member")) + + args = { + "users": users, + "nitk_count": nitk_count, + "ieee_count": ieee_count, + } + return render(request, "robotrix/admin/users.html", args) + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def announcements_management(request): + if request.method == "POST": + form = AnnouncementForm(request.POST) + if form.is_valid(): + announcement = form.save() + + mail_option = form.cleaned_data.get("announcement_mailing", "1") + email_ids = None + if mail_option == "2": + # just for team leaders + if announcement.announcement_type == "A": + email_ids = list( + Team.objects.values_list("team_leader__user__email", flat=True) + ) + elif announcement.announcement_type == "P": + email_ids = list( + Team.objects.filter(payment_status__in=["P", "E"]).values_list( + "team_leader__user__email", flat=True + ) + ) + elif announcement.announcement_type == "U": + email_ids = list( + Team.objects.filter(payment_status="U").values_list( + "team_leader__user__email", flat=True + ) + ) + elif mail_option == "3": + # for all members + if announcement.announcement_type == "A": + email_ids = list( + RobotrixUser.objects.values_list("user__email", flat=True) + ) + elif announcement.announcement_type == "P": + email_ids = list( + # send to both paid and exempted teams + RobotrixUser.objects.filter( + team__payment_status__in=["P", "E"] + ).values_list("user__email", flat=True) + ) + elif announcement.announcement_type == "U": + email_ids = list( + RobotrixUser.objects.filter( + team__payment_status="U" + ).values_list("user__email", flat=True) + ) + elif announcement.announcement_type == "N": + email_ids = list( + RobotrixUser.objects.filter(team=None).values_list( + "user__email", flat=True + ) + ) + elif announcement.announcement_type == "NI": + # all users who have not registered for robotrix + users = User.objects.exclude( + email__in=RobotrixUser.objects.values_list( + "user__email", flat=True + ) + ) + + users = users.exclude( + email__in=[ + "robotrix_admin", + "embedathon_admin", + ] + ) + users = users.exclude(is_staff=True) + users = users.exclude(is_superuser=True) + + email_ids = list(users.values_list("email", flat=True)) + + if email_ids is not None: + send_email( + "Announcement | Robotrix", + "emails/robotrix/announcement.html", + {"announcement": announcement}, + bcc=email_ids, + ) + + messages.success(request, "Successfully created announcement!") + return redirect("robotrix_announcements") + else: + messages.error( + request, "Please correct the errors before creating announcement!" + ) + return redirect("robotrix_announcements") + else: + form = AnnouncementForm() + announcements = Announcement.objects.all().order_by("-date_created") + + args = {"form": form, "announcements": announcements} + return render(request, "robotrix/admin/announcements.html", args) + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def delete_announcement(request, pk): + announcement = Announcement.objects.get(pk=pk) + announcement.delete() + messages.success(request, "Successfully deleted announcement!") + return redirect("robotrix_announcements") + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def mark_payment_complete(request, pk): + team = Team.objects.get(pk=pk) + team.payment_status = "P" + team.save() + for member in RobotrixUser.objects.filter(team=team): + if member.user.email is not None: + send_email( + "Payment Complete | Robotrix", + "emails/robotrix/payment_complete.html", + {"team": team, "user": member.user}, + bcc=[member.user.email], + ) + messages.success( + request, "Successfully marked payment as complete and sent emails!" + ) + return redirect("robotrix_admin_team_page", pk=pk) + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def mark_payment_incomplete(request, pk): + team = Team.objects.get(pk=pk) + team.payment_status = "U" + team.save() + for member in RobotrixUser.objects.filter(team=team): + if member.user.email is not None: + send_email( + "Payment Incomplete | Robotrix", + "emails/robotrix/payment_incomplete.html", + {"team": team, "user": member.user}, + bcc=[member.user.email], + ) + messages.success(request, "Successfully marked payment as incomplete!") + return redirect("robotrix_admin_team_page", pk=pk) + + +@login_required +@ensure_group_membership(group_names=["robotrix_admin"]) +def team_download(request): + import csv + from django.http import HttpResponse + + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = 'attachment; filename="teams.csv"' + + writer = csv.writer(response) + writer.writerow(["First Name", "Last Name", "Email", "Phone Number"]) + + for team in Team.objects.filter(payment_status__in=["P", "E"]): + leader = team.team_leader + writer.writerow( + [ + leader.user.first_name, + leader.user.last_name, + leader.user.email, + leader.user.phone_no, + ] + ) + + return response diff --git a/corpus/templates/components/general_dropdown.html b/corpus/templates/components/general_dropdown.html index b1617a3a..da21d2b7 100644 --- a/corpus/templates/components/general_dropdown.html +++ b/corpus/templates/components/general_dropdown.html @@ -27,4 +27,5 @@
  • Impulse
  • Electrika
  • Skyward Expedition
  • +
  • Robotrix
  • diff --git a/corpus/templates/components/navbar_large.html b/corpus/templates/components/navbar_large.html index 3d39c5c3..64d2ee1d 100644 --- a/corpus/templates/components/navbar_large.html +++ b/corpus/templates/components/navbar_large.html @@ -49,6 +49,11 @@ Skyward Expedition +
  • + + Robotrix + +
  • diff --git a/corpus/templates/robotrix/admin/admin.html b/corpus/templates/robotrix/admin/admin.html new file mode 100644 index 00000000..5b4d869a --- /dev/null +++ b/corpus/templates/robotrix/admin/admin.html @@ -0,0 +1,18 @@ +{% extends 'robotrix/base.html' %} + +{% block title %} + Admin + {{ block.super }} +{% endblock %} + +{% block content %} + +{% endblock %} diff --git a/corpus/templates/robotrix/admin/announcements.html b/corpus/templates/robotrix/admin/announcements.html new file mode 100644 index 00000000..e879e4e3 --- /dev/null +++ b/corpus/templates/robotrix/admin/announcements.html @@ -0,0 +1,162 @@ +{% extends 'robotrix/base.html' %} + +{% block title %} + Announcements Management + {{ block.super }} +{% endblock %} + +{% block content %} +
    +

    Announcements Management

    + +
    +
    + {% csrf_token %} +
    + + {{ form.content }} + {% if form.content.errors %} +
    + +
    + {% endif %} +
    + +
    + + {{ form.url_link }} + {% if form.url_link.errors %} +
    + +
    + {% endif %} +
    + +
    + + {{ form.url_link_text }} + {% if form.url_link_text.errors %} +
    + +
    + {% endif %} +
    + +
    + + {{ form.announcement_type }} + {% if form.announcement_type.errors %} +
    + +
    + {% endif %} +
    + +
    + + {{ form.announcement_mailing }} + {% if form.announcement_mailing.errors %} +
    + +
    + {% endif %} +
    + + + +
    +
    + +
    +

    Current Announcements

    + + + + + + + + + + + + + + {% if announcements %} + {% for announcement in announcements %} + + + + + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
    ContentURL LinkURL Link ContentAnnouncement TypeCreated AtDELETE
    {{ forloop.counter }}{{ announcement.content }}{{ announcement.url_link }}{{ announcement.url_link_text }}{{ announcement.get_announcement_type_display }}{{ announcement.date_created }} +
    + {% csrf_token %} + +
    +
    No announcements yet.
    +
    + Back +
    +{% endblock %} + +{% block script %} +{{ block.super }} + +{% endblock %} +``` diff --git a/corpus/templates/robotrix/admin/team_page.html b/corpus/templates/robotrix/admin/team_page.html new file mode 100644 index 00000000..136248c8 --- /dev/null +++ b/corpus/templates/robotrix/admin/team_page.html @@ -0,0 +1,61 @@ +{% extends 'robotrix/base.html' %} + +{% load static %} + +{% block title %} + {{ team.team_name }} | Team Management + {{ block.super }} +{% endblock %} + +{% block content %} +
    +

    {{ team.team_name }}

    +
    +

    Members

    +
    + + + + + + + + + + + + + + + {% for member in members %} + + + + + + + + + + {% comment %} Whether in team {% endcomment %} + + {% endfor %} + +
    NameEmailPhone NumberFrom NITK?College NameIEEE Member?IEEE Membership Number
    {{ forloop.counter }}{{ member.user }}{{ member.user.email }} {{ member.user.phone_no }}{{ member.from_nitk|yesno:"Yes,No" }}{{ member.college_name }}{{ member.ieee_member|yesno:"Yes,No" }}{{ member.ieee_membership_no }}
    +
    +
    +

    Payment status: {{ team.get_payment_status_display }}

    + + {% if team.payment_status == "U" %} + Mark Payment as Complete + {% endif %} + {% if team.payment_status == "P" %} + Mark Payment as Incomplete + {% endif %} + + Back +
    +{% endblock %} diff --git a/corpus/templates/robotrix/admin/teams.html b/corpus/templates/robotrix/admin/teams.html new file mode 100644 index 00000000..1d19110c --- /dev/null +++ b/corpus/templates/robotrix/admin/teams.html @@ -0,0 +1,47 @@ +{% extends 'robotrix/base.html' %} + +{% block title %} + Team Management + {{ block.super }} +{% endblock %} + +{% block content %} +
    +

    Team Management

    +

    Number of teams: {{ teams.count }}

    +
    + + + + + + + + + + + + {% for team in teams %} + + + + + + + + {% endfor %} + +
    NameLeaderPayment StatusActions
    {{ forloop.counter }}{{ team.team_name }}{{ team.team_leader.user }}{{ team.payment_status }} + View +
    +
    + + Back + +
    +{% endblock %} diff --git a/corpus/templates/robotrix/admin/users.html b/corpus/templates/robotrix/admin/users.html new file mode 100644 index 00000000..bbc03715 --- /dev/null +++ b/corpus/templates/robotrix/admin/users.html @@ -0,0 +1,60 @@ +{% extends 'base.html' %} + +{% block title %} + User Management | Admin + {{ block.super }} +{% endblock %} + +{% block content %} +
    +

    User Management

    +

    Number of Users:{{ users.count }}

    + +
    +

    Stats

    + NITK Students Stats + {% for item in nitk_count %} +

    {{ item.from_nitk | yesno:"From NITK, Not from NITK" }}: {{ item.count }}

    + {% endfor %} + IEEE Members Stats + {% for item in ieee_count %} +

    {{ item.ieee_member | yesno:"IEEE Member, Not IEEE Member" }}: {{ item.count }}

    + {% endfor %} +
    + +
    + + + + + + + + + + + + + + + + {% for user in users %} + + + + + + + + + + + + {% endfor %} + +
    NameEmailPhone NumberFrom NITK?College NameIEEE Member?IEEE Membership NumberIn a Team?
    {{ forloop.counter }}{{ user.user }}{{ user.user.email }}{{ user.phone_no }}{{ user.from_nitk|yesno:"Yes,No" }}{{ user.college_name }}{{ user.ieee_member|yesno:"Yes,No" }}{{ user.ieee_membership_no }}{{ user.team|yesno:"Yes,No" }}
    +
    + + Back +
    +{% endblock %} diff --git a/corpus/templates/robotrix/base.html b/corpus/templates/robotrix/base.html new file mode 100644 index 00000000..6f563575 --- /dev/null +++ b/corpus/templates/robotrix/base.html @@ -0,0 +1,24 @@ +{% extends 'base.html' %} + +{% block title %} +| Robotrix +{% endblock %} + +{% block script %} + +{% endblock %} diff --git a/corpus/templates/robotrix/home.html b/corpus/templates/robotrix/home.html new file mode 100644 index 00000000..1371d09f --- /dev/null +++ b/corpus/templates/robotrix/home.html @@ -0,0 +1,708 @@ +{% extends "robotrix/base.html" %} + +{% load static %} + +{% block title %} Home {{ block.super }} +{% endblock %} + +{% block style %} + + +{% endblock %} + +{% block content %} + + {% comment %} Hero Section {% endcomment %} +
    +

    + +

    Robotrix 2024


    + {% if admin %} + Admin + {% endif %} +

    24-Hour Robotics Hackathon



    + +

    NATIONAL INSTITUTE OF TECHNOLOGY KARNATAKA

    +

    24th February - 10th March 2024

    + {% if registration_active and not registration_done and not registered %} + Register Now + {% elif registration_done and not registered %} + Registrations Closed + {% elif registered %} + Go to Dashboard + {% else %} + Registrations Opening Soon + {% endif %} + +
    + +
    +
    + + {% comment %} About Section {% endcomment %} +
    +
    +
    + +
    +

    KNOW ABOUT ROBOTRIX

    +

    + Robotics is an emerging field in today's life. To introduce you to the field of robotics, we present Robotrix, a 24-hour + hackathon conducted by the IEEE NITK Student Branch in association with the IEEE NITK RAS Student Chapter. It presents + you with interesting problem statements to ensure a fun-filled learning experience. In addition to the hackathon, a + workshop will also be conducted before the final event to enhance your experience with us! +
    +

    Open to 1st, 2nd and 3rd year B.Tech students from all over India!

    +
    +

    +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    + +
    +
    + + + + {% comment %} Prizes Section {% endcomment %} +
    +
    +
    +

    Prizes

    +

    Exciting prizes to be won!

    +
    +
    + +
    + + + + + + +
    +

    Top 2 Teams receive Prizes worth 5000 INR !

    + {% comment %}

    Prizes and goodies to be announced soon !

    {% endcomment %} +
    + + + + +
    +


    + + {% comment %} Resources Section {% endcomment %} +
    +
    +
    +
    +

    + RESOURCES

    +
    +
    +
    +

    ROBOTRIX 2023 Problem Statement

    + + +
    +
    +
    +
    +
    +
    + + {% comment %} Schedule Section {% endcomment %} +
    +
    +
    +

    SCHEDULE

    +

    +
    +
      +
    • +
      + +
      +
      + +
      Expert talk
      + +
      +
      + +
    • +
    • +
      +
      + +
      +
      + +
      Qualifying test
      + +
      +
      + +
    • +
    • +
      +
      + +
      +
      + +
      Session 1 on Coppeliasim
      +
      +
      + +
    • +
    • +
      +
      + +
      +
      + +
      Assignment
      + +
      +
      + +
    • +
    • +
    • +
      +
      + +
      +
      + +
      Session 2 on Coppeliasim
      + +
      +
      + +
    • +
    • +
      +
      + +
      +
      + +
      24 hour Final hackathon
      + +
      +
    • + +
    +
    + + {% comment %} FAQ Section {% endcomment %} +
    +
    +
    +
    +
    + + FAQ + +

    + Any Questions? Look Here +

    +

    +

    +
    +
    +
    +
    +
    +
    + +
    +

    + 1st, 2nd or 3rd year B.Tech students from any engineering college and any branch in India are eligible to participate. +

    +
    +
    + +
    + +
    +

    +

      +
    1. No registration fees for NITK students
    2. +
    3. No registration fees for teams with at least one IEEE member
    4. +
    5. A registration fee of Rs.150 for teams having non-NITK and non-IEEE members
    6. +
    +

    +
    +
    + +
    + +
    +

    + A maximum of two members are allowed per team. +

    +
    +
    + +
    + +
    +

    + No. The questions contain aptitude, linear algebra and some python questions. It will not have any competitive coding style questions. +

    +
    +
    + +
    + +
    +

    + No, it's not necessary for a team to have an IEEE member in the team. Refer to eligibility of the event. +

    +
    +
    +
    + +
    +
    + +
    +

    + No, the workshop and talks are open for everyone who registers for the Hackathon. +

    +
    +
    + +
    + +
    +

    + Brushing up on your aptitude skills would be helpful. +

    +
    +
    + +
    + +
    +

    + Only those who clear the preliminary test would be eligible for the same. +

    +
    +
    + +
    + +
    +

    + The Complete event is in online mode including the hackathon. +

    +
    +
    +
    + +
    +
    +
    + + {% comment %} Organizers Section {% endcomment %} +
    +
    +
    +

    Organized by

    +
    +
    +
    + + IEEE NITK Logo + +
    +
    + + SPS Logo + +
    +
    +
    +
    +
    + +
    + + {% comment %} Contact Section {% endcomment %} +
    +
    +
    +

    Contact Us

    +
    +

    + Shivani Chanda : shivani.201me215@nitk.edu.in
    + Joel Jojo : joeljojopainuthara.211ec116@nitk.edu.in
    +

    +
    +
    +
    +
    +
    +{% endblock %} + +{% block script %} +{{ block.super }} + + + +{% endblock %} diff --git a/corpus/templates/robotrix/index.html b/corpus/templates/robotrix/index.html new file mode 100644 index 00000000..5a90db09 --- /dev/null +++ b/corpus/templates/robotrix/index.html @@ -0,0 +1,336 @@ +{% extends 'robotrix/base.html' %} + +{% load static %} + +{% block title %} +Robotrix +{% endblock %} + +{% block style %} + +{% endblock %} + +{% block content %} +
    +
    + +

    Robotrix

    +

    24-Hour Robotics Hackathon

    +

    NATIONAL INSTITUTE OF TECHNOLOGY KARNATAKA

    +

    24th February - 10th March 2024

    + {% comment %} Registrations Opening soon {% endcomment %} +
    +
    +
    + +
    +
    +
    +
    +
    +
    + {% if in_team %} +
    +
    +

    Team: {{ team.team_name }}

    + {% comment %} edit button {% endcomment %} + + + +
    +
    + Payment Status: {{ team.get_payment_status_display }} +
    +
    + + + + {% if team.payment_status == "U" %} +
    + + + + + Please pay the fee of Rs. 150 here . If you have already paid the fee, it might take some time for it to be updated here. Please only contact us if it has not been updated for more than a week. + +
    + {% endif %} +
    +
    +

    Team Members

    +
    + Team Size: {{ team_count }} / 2 +
    +
    + {% for member in members %} +
    +
    + {{ member.user }} +
    +
    + {% endfor %} +
    + {% if is_leader and registration_active and not team_full %} +
    +
    OR
    +
    +
    +

    Active Invites

    + {% if invites_from_team %} + + + + + + + + + + {% for invite in invites_from_team %} + + + + + + {% endfor %} + +
    Email AddressActions
    {{ forloop.counter }}{{ invite.invite_email }} + Revoke +
    + {% else %} +

    You have no active invites.

    + {% endif %} +

    Send Invite

    +
    + {% csrf_token %} +
    + + {{ invite_form.invite_email }} +

    Please enter the email that your teammate will + be registering with on Corpus.

    + {% if invite_form.invite_email.errors %} +
    + +
    + {% endif %} +
    +
    + +
    +
    +
    + {% endif %} + + {% elif registration_active %} + {% comment %} IMPORTANT DANGER BANNER {% endcomment %} +
    + + + + + YOU ARE NOT IN A TEAM. Please create a team or join an existing team to complete your registration for Robotrix. + +
    +
    +

    Current Invites

    + {% if invites_for_user %} + + + + + + + + + + + {% for invite in invites_for_user %} + + + + + {% endfor %} + +
    Team NameInvited ByActions
    {{ forloop.counter }}{{ invite.inviting_team.team_name }}{{ invite.inviting_team.team_leader }} + Accept + Reject +
    + {% else %} +

    You have no invites.

    + {% endif %} +
    +
    +
    OR
    +
    +
    +

    Create a team

    +
    + {% csrf_token %} + +
    + + {{ team_creation_form.team_name }} + {% if team_creation_form.team_name.errors %} +
    + +
    + {% endif %} +
    + +
    + +
    +
    +
    + If your team has members who are neither from NITK, nor IEEE members, you will have + to pay a registration amount of Rs. 150. This will be prompted + after you create a team. +
    +
    + + {% endif %} +
    +
    +
    +
    +

    Announcements

    +
    + {% if announcements %} +
      + {% for announcement in announcements %} +
    • + {{ announcement.content | linebreaks }} + {% if announcement.url_link %} + {{ announcement.url_link_text }} + {% endif %} +
    • + {% endfor %} +
    + {% else %} +

    You have no announcements as of now.

    + {% endif %} +
    +
    +
    +
    +
    +{% endblock %} + + +{% block script %} + {{ block.super }} + +{% endblock %} diff --git a/corpus/templates/robotrix/register.html b/corpus/templates/robotrix/register.html new file mode 100644 index 00000000..d93e07fe --- /dev/null +++ b/corpus/templates/robotrix/register.html @@ -0,0 +1,139 @@ +{% extends 'robotrix/base.html' %} + +{% block title %} + Register + {{ block.title }} +{% endblock %} + +{% block content %} +
    +

    Register for Robotrix

    +
    + {% csrf_token %} + + {% if form.non_field_errors %} + {% for error in form.non_field_errors %} + + {% endfor %} + {% endif %} + +
    + +
    + {{ form.from_nitk }} +
    + {% if form.from_nitk.errors %} +
    + +
    + {% endif %} +
    + +
    + + {{ form.college_name }} + {% if form.college_name.errors %} +
    + +
    + {% endif %} +
    + +
    + + {{ form.roll_no }} + {% if form.roll_no.errors %} +
    + +
    + {% endif %} +
    + +
    + + {{ form.phone_no }} + {% if form.phone_no.errors %} +
    + +
    + {% endif %} +
    + +
    + +
    + {{ form.ieee_member }} +
    + {% if form.ieee_member.errors %} +
    + +
    + {% endif %} +
    + +
    + + {{ form.ieee_membership_no }} + {% if form.ieee_membership_no.errors %} +
    + +
    + {% endif %} +
    + +
    + +
    +
    +
    +{% endblock %} + +{% block script %} + {{ block.super }} + +{% endblock %} diff --git a/corpus/templates/static/img/robotrix/robo_small.png b/corpus/templates/static/img/robotrix/robo_small.png new file mode 100644 index 00000000..3040938e Binary files /dev/null and b/corpus/templates/static/img/robotrix/robo_small.png differ diff --git a/corpus/templates/static/img/robotrix/robotrix_logo.png b/corpus/templates/static/img/robotrix/robotrix_logo.png new file mode 100644 index 00000000..d9bee017 Binary files /dev/null and b/corpus/templates/static/img/robotrix/robotrix_logo.png differ