From c41702cc4b1a0a9292f0444060868e1541115086 Mon Sep 17 00:00:00 2001 From: Vignaraj-pai Date: Fri, 12 Jan 2024 14:59:45 +0530 Subject: [PATCH 1/3] Add Electrika app and URL configuration --- corpus/corpus/settings.py | 1 + corpus/corpus/urls.py | 1 + corpus/electrika/__init__.py | 0 corpus/electrika/admin.py | 9 + corpus/electrika/apps.py | 6 + corpus/electrika/forms.py | 53 ++ corpus/electrika/migrations/__init__.py | 0 corpus/electrika/models.py | 60 ++ corpus/electrika/tests.py | 3 + corpus/electrika/urls.py | 46 + corpus/electrika/views.py | 520 ++++++++++ corpus/templates/base.html | 2 +- .../components/general_dropdown.html | 6 + corpus/templates/electrika/base.html | 26 + corpus/templates/electrika/home.html | 885 ++++++++++++++++++ corpus/templates/electrika/index.html | 357 +++++++ corpus/templates/electrika/register.html | 127 +++ corpus/templates/static/css/electrika.css | 218 +++++ .../static/img/electrika/electricaLogo.png | Bin 0 -> 38832 bytes .../static/img/electrika/first_prize.svg | 60 ++ .../templates/static/img/electrika/logo.svg | 1 + .../static/img/electrika/second_prize.svg | 62 ++ .../static/js/electrika/particles.json | 110 +++ 23 files changed, 2552 insertions(+), 1 deletion(-) create mode 100644 corpus/electrika/__init__.py create mode 100644 corpus/electrika/admin.py create mode 100644 corpus/electrika/apps.py create mode 100644 corpus/electrika/forms.py create mode 100644 corpus/electrika/migrations/__init__.py create mode 100644 corpus/electrika/models.py create mode 100644 corpus/electrika/tests.py create mode 100644 corpus/electrika/urls.py create mode 100644 corpus/electrika/views.py create mode 100644 corpus/templates/electrika/base.html create mode 100644 corpus/templates/electrika/home.html create mode 100644 corpus/templates/electrika/index.html create mode 100644 corpus/templates/electrika/register.html create mode 100644 corpus/templates/static/css/electrika.css create mode 100644 corpus/templates/static/img/electrika/electricaLogo.png create mode 100644 corpus/templates/static/img/electrika/first_prize.svg create mode 100644 corpus/templates/static/img/electrika/logo.svg create mode 100644 corpus/templates/static/img/electrika/second_prize.svg create mode 100644 corpus/templates/static/js/electrika/particles.json diff --git a/corpus/corpus/settings.py b/corpus/corpus/settings.py index ef8c7bd9..90c808b0 100644 --- a/corpus/corpus/settings.py +++ b/corpus/corpus/settings.py @@ -57,6 +57,7 @@ "accounts.apps.AccountsConfig", "embedathon.apps.EmbedathonConfig", "impulse.apps.ImpulseConfig", + "electrika.apps.ElectrikaConfig", ] MIDDLEWARE = [ diff --git a/corpus/corpus/urls.py b/corpus/corpus/urls.py index a561240f..7fc91599 100644 --- a/corpus/corpus/urls.py +++ b/corpus/corpus/urls.py @@ -24,4 +24,5 @@ path("accounts/", include("accounts.urls")), path("embedathon/", include("embedathon.urls")), path("impulse/", include("impulse.urls")), + path("electrika/", include("electrika.urls")), ] diff --git a/corpus/electrika/__init__.py b/corpus/electrika/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/corpus/electrika/admin.py b/corpus/electrika/admin.py new file mode 100644 index 00000000..7655248f --- /dev/null +++ b/corpus/electrika/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +# Register your models here. +from .models import ElectrikaUser, Team, Announcement, Invite + +admin.site.register(ElectrikaUser) +admin.site.register(Team) +admin.site.register(Announcement) +admin.site.register(Invite) \ No newline at end of file diff --git a/corpus/electrika/apps.py b/corpus/electrika/apps.py new file mode 100644 index 00000000..443fb73b --- /dev/null +++ b/corpus/electrika/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ElectrikaConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'electrika' diff --git a/corpus/electrika/forms.py b/corpus/electrika/forms.py new file mode 100644 index 00000000..dc731481 --- /dev/null +++ b/corpus/electrika/forms.py @@ -0,0 +1,53 @@ +from electrika.models import ElectrikaUser, Team, Announcement, Invite +from django import forms +from corpus.forms import CorpusModelForm + +class ElectrikaForm(CorpusModelForm): + class Meta: + model = ElectrikaUser + fields = [ + "from_nitk", + "college_name", + "roll_no", + "ieee_member", + "ieee_membership_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"] \ No newline at end of file diff --git a/corpus/electrika/migrations/__init__.py b/corpus/electrika/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/corpus/electrika/models.py b/corpus/electrika/models.py new file mode 100644 index 00000000..78813852 --- /dev/null +++ b/corpus/electrika/models.py @@ -0,0 +1,60 @@ +from django.db import models +from embedathon.models import PAYMENT_STATUS +from accounts.models import User + +class ElectrikaUser(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) + 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( + ElectrikaUser, 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 Electrika Users"), + ("P", "Paid Teams"), + ("U", "Unpaid Teams"), + ("N", "Registered for Electrika but no team"), + ) + + 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 \ No newline at end of file diff --git a/corpus/electrika/tests.py b/corpus/electrika/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/corpus/electrika/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/corpus/electrika/urls.py b/corpus/electrika/urls.py new file mode 100644 index 00000000..6902d3e9 --- /dev/null +++ b/corpus/electrika/urls.py @@ -0,0 +1,46 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path("", views.home, name="electrika_home"), + path("index", views.index, name="electrika_index"), + path("register", views.register, name="electrika_register"), + path("create_team", views.create_team, name="electrika_create_team"), + path("create_invite", views.create_invite, name="electrika_create_invite"), + path( + "accept_invite/", views.accept_invite, name="electrika_accept_invite" + ), + path( + "delete_invite/", views.delete_invite, name="electrika_delete_invite" + ), + path("admin", views.admin, name="electrika_admin"), + path("admin/teams", views.team_management, name="electrika_admin_teams"), + path("admin/team/", views.team_page, name="electrika_admin_team_page"), + path( + "admin/team//mark_payment_complete", + views.mark_payment_complete, + name="electrika_admin_mark_payment_complete", + ), + path( + "admin/team//mark_payment_incomplete", + views.mark_payment_incomplete, + name="electrika_admin_mark_payment_incomplete", + ), + path("admin/users", views.user_management, name="electrika_admin_users"), + path( + "admins/announcements", + views.announcements_management, + name="electrika_announcements", + ), + path( + "admin/announcements/delete/", + views.delete_announcement, + name="electrika_delete_announcement", + ), + path( + "admin/download_csv", + views.download_csv_non_registrants, + name="electrika_download_csv", + ), + +] \ No newline at end of file diff --git a/corpus/electrika/views.py b/corpus/electrika/views.py new file mode 100644 index 00000000..50c70935 --- /dev/null +++ b/corpus/electrika/views.py @@ -0,0 +1,520 @@ +from datetime import datetime +from django.shortcuts import render +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.shortcuts import redirect +from electrika.models import Announcement +from electrika.models import ElectrikaUser +from electrika.models import Team +from electrika.models import Invite +from electrika.forms import ElectrikaForm +from electrika.forms import TeamCreationForm +from electrika.forms import AnnouncementForm +from electrika.forms import InviteForm +from corpus.decorators import ensure_group_membership +from corpus.decorators import module_enabled +from corpus.utils import send_email + +# Create your views here. +@module_enabled(module_name="electrika") +def home(request): + args = {} + + # Checking if user is Electrika_admin group member + if request.user.groups.filter(name="electrika_admin").exists(): + args = {"admin": True} + config = ModuleConfiguration.objects.get(module_name="electrika").module_config + + try: + if request.user.is_authenticated: + electrika_user = ElectrikaUser.objects.get(user=request.user) + args["registered"] = True + except ElectrikaUser.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, + "electrika/home.html", + args, + ) + +@login_required +@module_enabled(module_name="electrika") +def index(request): + args = {} + try : + electrika_user = ElectrikaUser.objects.get(user=request.user) + args["electrika_user"] = electrika_user + except ElectrikaUser.DoesNotExist: + messages.error(request, "Please register for Electrika first!") + return redirect("electrika_user") + + if electrika_user.team is not None: + args["in_team"] = True + + args["team_creation_form"] = TeamCreationForm(instance=electrika_user.team) + + team = electrika_user.team + members = ElectrikaUser.objects.filter(team=team) + team_count = members.count() + + config = ModuleConfiguration.objects.get(module_name="electrika").module_config + + max_count = int(config["max_team_size"]) + + if team_count >= max_count: + args["team_full"] = True + if team.team_leader == electrika_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=electrika_user.user.email) + args["invites_for_user"] = invites + + + config = ModuleConfiguration.objects.get(module_name="electrika").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: + announcements = Announcement.objects.filter(announcement_type__in=["A", "N"]) + + announcements = announcements.order_by("-date_created") + + args["announcements"] = announcements + + return render(request, "electrika/index.html", args) + + +@login_required +@module_enabled(module_name="electrika") +def register(request): + + config = ModuleConfiguration.objects.get(module_name="electrika").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 Electrika is not active yet!") + return redirect("index") + + try: + electrika_user = ElectrikaUser.objects.get(user=request.user) + messages.error(request, "You have already registered for Electrika!") + return redirect("electrika_index") + except ElectrikaUser.DoesNotExist: + pass + + if request.method == "POST": + form = ElectrikaForm(request.POST) + if form.is_valid(): + electrika_user = form.save(commit=False) + electrika_user.user = request.user + electrika_user.save() + messages.success(request, "Successfully registered for Electrika!") + return redirect("electrika_index") + else: + messages.error(request, "Please correct the errors before registering!") + + else: + form = ElectrikaForm() + + args = {"form": form} + return render(request, "electrika/register.html", args) + + +@login_required +@module_enabled(module_name="electrika") +def create_team(request): + if request.method == "POST": + form = TeamCreationForm(request.POST) + electrika_user = ElectrikaUser.objects.get(user=request.user) + if electrika_user.team is not None: + if form.is_valid(): + team = electrika_user.team + team.team_name = form.cleaned_data["team_name"] + team.save() + messages.success(request, "Successfully updated team name!") + return redirect("electrika_index") + else: + if form.is_valid(): + team = form.save(commit=False) + electrika_user = ElectrikaUser.objects.get(user=request.user) + team.team_leader = electrika_user + + if electrika_user.from_nitk or electrika_user.ieee_member: + team.payment_status = "E" + else: + team.payment_status = "U" + + team.save() + electrika_user.team = team + electrika_user.save() + messages.success(request, "Successfully created team!") + return redirect("electrika_index") + else: + messages.error(request, "Please correct the errors before creating team!") + return redirect("electrika_index") + + +@login_required +@module_enabled(module_name="electrika") +def create_invite(request): + electrika_user = ElectrikaUser.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("electrika_index") + + try: + user = User.objects.get(email=form.cleaned_data["invite_email"]) + invited_imp_user = ElectrikaUser.objects.get(user=user) + if invited_imp_user.team is not None: + messages.error(request, "User is already in a team!") + return redirect("electrika_index") + except (User.DoesNotExist, ElectrikaUser.DoesNotExist): + pass + + try: + invite = Invite.objects.get( + inviting_team=electrika_user.team, + invite_email=form.cleaned_data["invite_email"], + ) + messages.error(request, "Invite has already been sent!") + return redirect("electrika_index") + except Invite.DoesNotExist: + pass + + invite_counts = Invite.objects.filter( + inviting_team=electrika_user.team + ).count() + + team_members = ElectrikaUser.objects.filter(team=electrika_user.team).count() + + config = ModuleConfiguration.objects.get( + module_name="electrika" + ).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("electrika_index") + + invite = form.save(commit=False) + inviting_team = electrika_user.team + invite.inviting_team = inviting_team + invite.save() + + messages.success(request, "Invite sent!") + return redirect("electrika_index") + messages.error(request, "Illegal Request") + return redirect("electrika_index") + +@login_required +@module_enabled(module_name="electrika") +def accept_invite(request, pk): + invite = Invite.objects.get(pk=pk) + team_members = ElectrikaUser.objects.filter(team=invite.inviting_team).count() + config = ModuleConfiguration.objects.get(module_name="electrika").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("electrika_index") + + if request.user.email != invite.invite_email: + messages.error(request, "Illegal request") + return redirect("electrika_index") + + electrika_user = ElectrikaUser.objects.get(user=request.user) + electrika_user.team = invite.inviting_team + electrika_user.save() + + if electrika_user.from_nitk or electrika_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("electrika_index") + + +@login_required +@module_enabled(module_name="electrika") +def delete_invite(request, pk): + invite = Invite.objects.get(pk=pk) + invite.delete() + + messages.success(request, "Invite deleted!") + return redirect("electrika_index") + + +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def admin(request): + return render(request, "electrika/admin/admin.html", {}) + +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def team_management(request): + args = {} + args["teams"] = Team.objects.all() + return render(request, "electrika/admin/teams.html", args) + +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def team_page(request, pk): + args = {} + team = Team.objects.get(pk=pk) + args["team"] = team + args["members"] = ElectrikaUser.objects.filter(team=team) + return render(request, "electrika/admin/team_page.html", args) + +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def user_management(request): + args = {} + args["users"] = ElectrikaUser.objects.all() + return render(request, "electrika/admin/users.html", args) + +@login_required +@ensure_group_membership(group_names=["electrika_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="P").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( + ElectrikaUser.objects.values_list("user__email", flat=True) + ) + elif announcement.announcement_type == "P": + email_ids = list( + ElectrikaUser.objects.filter(team__payment_status="P").values_list( + "user__email", flat=True + ) + ) + elif announcement.announcement_type == "U": + email_ids = list( + ElectrikaUser.objects.filter(team__payment_status="U").values_list( + "user__email", flat=True + ) + ) + elif announcement.announcement_type == "N": + email_ids = list( + ElectrikaUser.objects.filter(team=None).values_list( + "user__email", flat=True + ) + ) + elif announcement.announcement_type == "NI": + # all users who have not registered for electrika + users = User.objects.exclude( + email__in=ElectrikaUser.objects.values_list("user__email", flat=True) + ) + + users = users.exclude( + email__in=[ + "electrika_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 | Electrika", + "emails/electrika/announcement.html", + {"announcement": announcement}, + bcc=email_ids, + ) + + messages.success(request, "Successfully created announcement!") + return redirect("electrika_announcements") + else: + messages.error(request, "Please correct the errors before creating announcement!") + return redirect("electrika_announcements") + else: + form = AnnouncementForm() + announcements = Announcement.objects.all().order_by("-date_created") + + args = {"form": form, "announcements": announcements} + return render(request, "electrika/admin/announcements.html", args) + +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def delete_announcement(request, pk): + announcement = Announcement.objects.get(pk=pk) + announcement.delete() + messages.success(request, "Successfully deleted announcement!") + return redirect("electrika_announcements") + +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def mark_payment_complete(request, pk): + team = Team.objects.get(pk=pk) + team.payment_status = "P" + team.save() + for member in ElectrikaUser.objects.filter(team=team): + if member.user.email != None: + send_email( + "Payment Complete | Electrika", + "emails/electrika/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("electrika_admin_team_page", pk=pk) + +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def mark_payment_incomplete(request, pk): + team = Team.objects.get(pk=pk) + team.payment_status = "U" + team.save() + for member in ElectrikaUser.objects.filter(team=team): + if member.user.email != None: + send_email( + "Payment Incomplete | Electrika", + "emails/electrika/payment_incomplete.html", + {"team": team, "user": member.user}, + bcc=[member.user.email], + ) + messages.success(request, "Successfully marked payment as incomplete!") + return redirect("electrika_admin_team_page", pk=pk) + + +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def download_csv_non_registrants(request): + import csv + from django.http import HttpResponse + + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = 'attachment; filename="non_registrants.csv"' + + writer = csv.writer(response) + writer.writerow(["Name", "Email"]) + + users = User.objects.exclude( + email__in=ElectrikaUser.objects.values_list("user__email", flat=True) + ) + + users = users.exclude( + email__in=[ + "electrika_admin", + "embedathon_admin", + ] + ) + users = users.exclude(is_staff=True) + users = users.exclude(is_superuser=True) + + + for user in users: + writer.writerow([user, user.email]) + + return response diff --git a/corpus/templates/base.html b/corpus/templates/base.html index 8b659927..2f832bbc 100644 --- a/corpus/templates/base.html +++ b/corpus/templates/base.html @@ -23,7 +23,7 @@ + + +{% endblock %} diff --git a/corpus/templates/electrika/home.html b/corpus/templates/electrika/home.html new file mode 100644 index 00000000..1843709b --- /dev/null +++ b/corpus/templates/electrika/home.html @@ -0,0 +1,885 @@ +{% extends "electrika/base.html" %} + +{% load static %} + +{% block title %} + Home + {{ block.super }} +{% endblock %} + +{% block style %} + + + +{% endblock %} + +{% block content %} + + {% comment %} Hero Section {% endcomment %} +
+
+
+

IEEE NITK

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

POWER ELECTRONICS HACKATHON

+ {% comment %}

Theme: Explainable AI

{% endcomment %} +

23rd January 2024 - 3rd Feb 2024

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

Unveiling Electrika

+

+ Electrika - 'The Power Electronics Hackathon,' first of a kind conducted by The Industrial and Application Society of IEEE NITK Student Branch . Our event is designed to offer participants a profound understanding of power electronics while making the process of designing power electronics circuits an enjoyable and educational experience. We aim to acquaint participants with the intricacies of designing these circuits, making learning both engaging and informative.
+

Open to 2nd 3rd and 4th year Btech students across india!

+
+

+
+
+
+
+ +
+
+
+
+

+ Speakers

+ {% comment %}
+
+
+ Vineeth N Balasubramanian +
+
+

Breaking the Black Box: Explainability of AI/ML Models for Trustworthy Systems

+ By Vineeth N Balasubramanian + Vineeth N Balasubramanian is a Professor in the Department of Computer Science and Engineering at IIT-Hyderabad, India, and a recent Fulbright-Nehru Visiting Faculty Fellow at Carnegie Mellon University until July 2023. He served as the Founding Head of the Department of Artificial Intelligence at IIT-H from 2019-22. His research focuses on deep learning, machine learning, and computer vision, particularly in explainability, continual learning, and limited labeled data scenarios. He has received numerous accolades, including the Research Excellence Award at IIT-H (2022), Google Research Scholar Award (2021), and NASSCOM AI Gamechanger Award (2022). He is listed among the World's Top 2% Scientists and actively contributes to premier conferences such as CVPR, ICCV, AAAI, IJCAI, ECCV in various senior roles. +
+ 11th Jan, 2024 +
+
+
+
+
+ Pranav Koundinya +
+
+

Workshop on Grad-CAM

+ By Pranav Koundinya + Chairperson of the Signal Processing Society, IEEE-NITK Student Branch + A final year undergrad in the ECE department, he has worked as a research intern under Prof. Chandra Sekhar Seelamantula at the Spectrum Lab, Indian Institute of Science, Bangalore, where he has worked on the analysis and development of visual anomaly detection models for industrial applications. His interests lie in the fields of image signal processing, computer vision and deep learning. +
+ 12th Jan, 2024 +
+
+
+
{% endcomment %} + {% comment %} To be announced soon {% endcomment %} +

To be announced soon

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

Prizes

+

Exciting prizes to be won!

+
+
+ +
+ + + + +
+
+

Top 2 Teams receive cash prizes from a pool of 5000 INR

+
+ + + + + + + + + + + + + + + +
+
+
+
+ {% comment %} First prize cup icon svg {% endcomment %} + First Prize +
+
+
+
+ First Prize +
+ {% comment %} Desktop Support Technician {% endcomment %} +
3000 INR
+
+
+
+ {% comment %} Second prize cup icon svg {% endcomment %} + Second Prize +
+
+
+
+ Second Prize +
+
2000 INR
+
+ {% comment %}

Prizes and goodies to be announced soon !

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

SCHEDULE

+

+
+
    +
  • +
    +
    +
    +
    + +
    Expert Talk
    + +
    +
    +
  • +
  • +
    +
    +
    +
    +
    + +
    Preliminary test
    + +
    +
    +
  • +
  • +
    +
    +
    +
    +
    + +
    Knowledge sharing sessions and Tasks
    + +
    +
    +
  • +
  • +
    +
    +
    +
    +
    + +
    Main problem statement reveal
    + +
    +
    +
  • +
  • +
    +
    +
    +
    +
    + +
    Hardware Round
    + +
    +
    +
  • +
+
+ + {% comment %} FAQ Section {% endcomment %} +
+
+
+
+
+ + FAQ + +

+ Any Questions? Look Here +

+

+

+
+
+
+
+
+
+ +
+

+ All students from all institutes can apply for this event. +

+
+
+ +
+ +
+

+ No, Registration is free for everyone participating. +

+
+
+ +
+ +
+

+ Top 2 teams will receive cash prizes from a pool of Rs 5000. +

+
+
+ +
+ +
+

+ It is not necessary to be from ECE or EEE background. Students with interest and knowledge in the basics of Power Electronics can apply for the event irrespective of their background. +

+
+
+ +
+ +
+

+ To prepare for the competition, participants can review various application notes on prevalent design topologies available online. Understanding these notes will aid in preparing the required design documentation, a key objective for all teams to successfully accomplish. Additionally, exploring YouTube videos and research papers can offer valuable insights. +

+
+
+ +
+ +
+

+ Yes, the final event will take place offline during working hours at NITK. Selected teams are expected to cover their own transportation expenses. +

+
+
+ +
+ +
+

+ Round-1 will consist of two subtasks given after both the knowledge-sharing sessions are conducted. They will cover the basics of power electronics and embedded systems. +

+
+
+ +
+ +
+

+ It will entail the main problem statement, set to be released on Jan 31st, with submissions due by Feb 2nd at 12 PM. +

+
+
+ +
+ +
+
+ +
+

+ No. The test will have questions about the basics of Linear Algebra, Signals and Systems, Machine Learning, Aptitude and some Python coding. It will not have any competitive coding style questions. +

+
+
+ +
+ +
+

+ A team should can consist a maximum of 4 members. +

+
+
+ +
+ +
+

+ No, It isn't necessary to have an IEEE member in a team comprising Non-NITK Members +

+
+
+ +
+ +
+

+ Both round 1 and round 2 have separate sets of criteria which will be clearly mentioned in the problem statement. And in all cases, the organizers' decision will be the final decision. +

+
+
+ +
+ +
+

+ You'll gain insights into designing industrial-grade power converters and implementing hardware of the designed converter. +

+
+
+ +
+ +
+

+ It's scheduled for a single day on February 3. +

+
+
+ +
+ +
+

+ Yes, All teams qualifying round-1 will receive certificates. +

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

Organized by

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

Contact Us

+
+

+ Apurva S : apurva.indumathi@gmail.com
+ {% comment %} Raghuram Kannan : raghuramkannan400@gmail.com
+ Kaliki Venkata Srinanda : srinandakv2004@gmail.com {% endcomment %} +

+
+
+
+
+
+{% endblock %} + +{% block script %} +{{ block.super }} + + + + + + + +{% endblock %} diff --git a/corpus/templates/electrika/index.html b/corpus/templates/electrika/index.html new file mode 100644 index 00000000..81597b94 --- /dev/null +++ b/corpus/templates/electrika/index.html @@ -0,0 +1,357 @@ +{% extends 'electrika/base.html' %} + +{% load static %} + +{% block title %} +Impulse +{% endblock %} + +{% block style %} + +{% endblock %} + +{% block content %} +
+
+
+

IEEE NITK

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

POWER ELECTRONICS HACKATHON

+ {% comment %}

Theme: Explainable AI

{% endcomment %} +

23rd January 2024 - 3rd Feb 2024

+
+ +
+
+ +
+
+
+
+
+
+ {% if in_team %} +
+
+

Team: {{ team.team_name }}

+ {% comment %} edit button {% endcomment %} + + + +
+
+ Payment Status: {{ team.get_payment_status_display }} +
+
+ + {% if team.payment_status == "P" or team.payment_status == "E" %} +
+

Join the Discord Server

+ + + + + +
+ {% endif %} + + {% 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 Impulse. + +
+
+

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/electrika/register.html b/corpus/templates/electrika/register.html new file mode 100644 index 00000000..a81c3074 --- /dev/null +++ b/corpus/templates/electrika/register.html @@ -0,0 +1,127 @@ +{% extends 'electrika/base.html' %} + +{% block title %} + Register + {{ block.title }} +{% endblock %} + +{% block content %} +
+

Register for Electrika

+
+ {% 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.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 %} \ No newline at end of file diff --git a/corpus/templates/static/css/electrika.css b/corpus/templates/static/css/electrika.css new file mode 100644 index 00000000..5c35a8a3 --- /dev/null +++ b/corpus/templates/static/css/electrika.css @@ -0,0 +1,218 @@ + +/* ---------------------------------- +Electrika Title +---------------------------------- */ +.wrapper { + /* height: 800px; */ + display: flex; + align-items: center; + justify-content: center; + background: #000000; + padding-bottom: 1rem; + } + +.txt { + color: #ffffff; + background:#000000; + font-size:60px; + font-weight: bold; + font-family: Arial; + text-transform: uppercase; +} + +@media (min-width : 768px) { + .txt { + font-size : 80px; + } +} + +@media (min-width : 992px) { + .txt { + font-size : 90px; + } +} + +@media (min-width : 1200px) { + .txt { + font-size : 100px; + } +} + +.txt::before { + content: 'ELECTRIKA'; + position: absolute; + mix-blend-mode: difference; + filter: blur(0.8px); +} + +.neon-wrapper { + display:inline-flex; + filter: brightness(200%); + overflow: hidden; +} + +.gradient{ + /* background: linear-gradient(90deg, #2196f3 0%, rgba(33,150,243,1) 100%); */ + background: linear-gradient(90deg, rgba(33,150,243,1) 30%, rgba(3,233,244,1) 64%); + /* background: radial-gradient(circle, rgba(188,70,252,1) 0%, rgba(33,150,243,1) 73%); */ + /* background: linear-gradient(114.5793141156962deg, rgba(6, 227, 250,1) 4.927083333333334%,rgba(229, 151, 64,1) 97.84374999999999%); */ + position: absolute; + top: 0; + left:0; + width: 100%; + height:100%; + mix-blend-mode: multiply; +} + +.dodge { + background: radial-gradient(circle,white,black 35%) center / 25% 25%; + position: absolute; + top:-100%; + left:-100%; + right:0; + bottom:0; + mix-blend-mode: color-dodge; + animation: dodge-area 3s linear infinite; +} + +@keyframes dodge-area { + to { + transform: translate(50%,50%); + } +} + + +/* ------------------------------- +Register button +------------------------------- */ +.registerBtn { + position: relative; + display: inline-block; + padding: 20px 25px; + margin: 5px 20px; + color: #03e9f4; + font-size: 16px; + text-decoration: none; + text-transform: uppercase; + overflow: hidden; + transition: 0.5s; + letter-spacing: 4px; + /* -webkit-box-reflect: below 1px linear-gradient(transparent, #0005); */ + font-family: consolas; +} + +@media (min-width : 768px) { + .registerBtn { + font-size : 20px; + } +} + +@media (min-width : 992px) { + .registerBtn { + font-size : 22px; + } +} + +@media (min-width : 1200px) { + .registerBtn { + font-size : 24px; + } +} + +.registerBtn:hover { + -webkit-box-reflect: below 1px linear-gradient(transparent, #0005); + background: #03e9f4; + color: #050801; + box-shadow: 0 0 5px #03e9f4, + 0 0 25px #03e9f4, + 0 0 50px #03e9f4, + 0 0 200px #03e9f4; +} + +.registerBtn span { + position: absolute; + display: block; +} + +.registerBtn span:nth-child(1) { + top: 0; + left: 0; + width: 100%; + height: 2px; + background: linear-gradient(90deg, transparent, #03e9f4); + animation: animate1 1s linear infinite; +} + +@keyframes animate1 { + 0% + { + left: -100%; + } + 50%, 100% + { + left: 100%; + } +} + +.registerBtn span:nth-child(2) { + top: -100px; + right: 0; + width: 2px; + height: 100%; + background: linear-gradient(180deg, transparent, #03e9f4); + animation: animate2 1s linear infinite; + animation-delay: 0.25s; +} + +@keyframes animate2 { + 0% + { + top: -100%; + } + 50%, 100% + { + top: 100%; + } +} + +.registerBtn span:nth-child(3) { + bottom: 0; + right: -100%; + width: 100%; + height: 2px; + background: linear-gradient(270deg, transparent, #03e9f4); + animation: animate3 1s linear infinite; + animation-delay: 0.5s; +} + +@keyframes animate3 { + 0% + { + right: -100%; + } + 50%, 100% + { + right: 100%; + } +} + +.registerBtn span:nth-child(4) { + bottom: -100%; + left: 0; + width: 2px; + height: 100%; + background: linear-gradient(360deg, transparent, #03e9f4); + animation: animate4 1s linear infinite; + animation-delay: 0.75s; +} + +@keyframes animate4 { + 0% + { + bottom: -100%; + } + 50%, 100% + { + bottom: 100%; + } +} diff --git a/corpus/templates/static/img/electrika/electricaLogo.png b/corpus/templates/static/img/electrika/electricaLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..21f24d0cff1c40736640dfa371e272ef8e8c5ccf GIT binary patch literal 38832 zcmeFZhhLM~^9IW5f~y$W6%`2}c0dpmB1jiek=~RlMx}->y+=gCS^(+NK?p?zq<2tJ z>BZ2ybSWV~fBJ<|chhsvewhZ_SmItaz*O=(geR&cqf>c z`+i3sI&9}%Z2fgGK0*AL_t*Z6XYzyH;u|s1PyW8h&-^w`PhI=Pt>7CMuLoxzej9%3 zaN31udTIzGRoI7>V+slvM!)cXVU4qTMP95hTM{Ev!fJ6Nr%!vMNk-l#8)T{%?Y2d) zQBhGZabUqyBO5vt6VtWCiN6_tV7jleoAHOgGZ8!Ac=-DpkRg8Cw+p z63GIc`nc&ruY(-*MNxI{|2}2-=EaNiM%9!xvG&1o)bq4IuBU?^@01cN>hboPsa+k3 zrB|EIV>;CCP@gL5`#3HhCUE+W_nhLW#;*3|^GYoo4nS|>{2OZCp}&q%Ngfktc58ZW zee06gpIGO)x#hrX=#KwBE%Cyn#FSELd{)kHsGFlswuY2hl*Z0_K!oXvg56L75jculi-xJWkIAr3QCD*ZBrgd$49R8xs@rJF~gCdh9rK!cx=B-Ma$Q zzzJWzP}F60=Y2(;;?d=jzpw$$vMn#Ni0~@EY$WwBB*ZZLQwGvBaeTma36K~?l7uj z2+ruK!OC|ulde97tn>T+j4foO#KFR8@T4je(?HgX7d%m%8uF8GT^A&zMR$JQRT5FM zJY6FOou*-dg}1P>EX9Ak|6$!>*--EMbk@;}g$sGv-a*x1NTO56Sh|eY7!C}6my^@k zG35&6AsS{DrGq7wUR2jK@Dpg%{72tlC7CKhg+q?dCTm2m-YzDq;K7;{n}*(Y{DA|9 z{>3pdy^zJXw43T|?2vSsSomR2LRS|icsy&RoMWSIiE28wz4DfIH`DQlJT?-;`l!pm zHSDW=LlytLo(isR8CkoDpQ}U9Hr6Ni@Lk}(bj(qs<5_S$FYRNxJ~tCncp?4$2X-?S z@>RtGe&9-Y=w`;`B(~OIiqAeOHxTl9`dubBJN7J2086~h$*ZM(Vz@`42;xv6I8$e> zSI}%e?Nchfi^W)$Pug$#aczu=^mbx$Bw88U+}LkdM!2{t)>_J>b@urY5qT7R_ikMni6kYExD&gppZ7F?S~JrlajpL08nk19`4jW8nGuvJF?2 zR~`;l)QEUAG$xKE3#_<00ngIdrZ^NkP1#kc(;Ma4rcu0jfFVk}|DF)+*j6p~;J^FqPeAUrt#aP?8kR^%`9en&4L8 zQklO6-+N%}mNmAvnF1n`I)5FmC^`vzG>Kf=Tt*O>CBsw#aNqVxez+Wd(#;h=3g85d zTqPp(KUgus`WF01rwX{|^fjfC z0=EG9rnLkY5v$o|jGdYPS?2^Nw@^kJr<7n9; zk}H9Cfk(K*@9Ruh6pQ{%v7Y-wA&I5ETJT8Bt)2>s#$tZ|n8Tai+_B+K&XxJ2zZ~yN z<%Sx)NpO#t$iC3ZcZ8=g`(aqb<<}{KK{a1G~}FU!Ml@j!BnQd2gE}%eZ_s7Sy~e;juktR%Fx-0MyT` zqZoIOAND1zz@BPB@&`BbGR0g_dN}ULt#9U$*Pq8o+ovENK_3BsbjYaUoDr&GI)b=Buf9G72lTko1VLyONVS#=(ntwi) z3@>|EI_n7tq`et*trn5ZuE+=yHV_8Q2;6>VO{;_Rjhpjfov-PuY-%YsyPz{Ry*OqD_3gSNStH$zw*l?%%(a&3XY0xKXNu&+5U*Ewy^;J zfE@JJuO?mtc6n#;WpWJ8zpR~*O$)`}v>|_2GU+e#C8aZ*>S3ME$V`OQl3*l;{rhN@2-YJfY`s`nu0N@#Q_Le*zp5#{Vx{IN25#?7!4b`#}9XLkx`C)wTY|WKa(C%mRjZO9Y>>ZLH;g}#J zLXMk?m}>d{=_70YF=bO0pA}V>JbL%1<;}+dKK*U(8o7Rxj_~KYiJk-ZNU{}oUpC7W z0z_PKf^co>6R^6@wsX-My5)39!wu4??}4+wTK?d2Vsa~OrE;pyhEAu}e^aVMNM1_2 z$lUV^g|+z{KCb zfKJF_x$COF@OM?WS~6-ONUqS^N#Vie*!)phXAsG3cM}~QJVwY37S0tKQ9EIUHz@Y- ztU5nGQF|42SpApVl=juMA~y_hWn}m5Knt z3txPCnV?=DkVa&9V1tQrQ&$46vmJXP`;2=C516zD!U=u&Y^Z(Jmu+J zU`W{+$tvG3#O-`w-`2N^Tx$akYV?8a)IU2Kc}=7oN&Y^GY{W*D0KwCIX{LCL_|~p` z@;>Z1KUg-CWI1h-VP1;wk6oNIE_4Y_+qd%oa0~n}46gHCQtDQw!%bTqa!k@dRjRY? zJ+o1NJKV6(IyE`z@}wtKIWZ1dlSN=1ixUsE<%iuE$~#K{R>54)r;3Hgj5B_&2S{X zQNWuR)F4I=-r&9L(bLm6>vQ@N8X)`(gy1CInMfINN zzf9Oao;`G^1fLtSA2U6DNljA_W-c3bL#MtkEM#Gb*o$ZA!gB&|mMK9+rz?aHo6`!A}h3s>pERtn7 ze&1xbD$4Pk>Q>=_iuJIO2(~*>?{BTYk=RdPPSNKy!gqFm1Sn5FQ{Y@LZ_DE(wEq0|uOOD%1e5&F>60B^D@WHFJx=};7058=QbPN%D= zlqbXS;RmrFvTwnyVnY{`d^+D)j9Fa9508ROX0MOgkFT_uFOxdhZ#p?mIKrhdC5WVA z&?!YY40gO6n6MnMFu=M+3@bCD3hoayhphTa4|Uzi-4KES&oxG6A}UEss^o}HCxe%~ zD!b229eEPT57WTnuGpMj6|V=p>e_F+DhoeWhbg&4sn;<>`oB%F|GIZic zGEM+fRRrG>sNpLHkidrTRdo=t0+jZE04(yQx311>#N{ZJ-RGBik+IftVhN;-aqplR zp4J2dgRI)|u`h99K5N?e0GIdN=_%|#EuZhudSbQo&F%k32rk%@@RK%^Jr+5s0+ zzfpGA-0bb8Hdo+lA>vd1xGQ@rB+i^s3DC_(ye!ou#M=9@en(Tjy*eD>^xd>fLw5CX z?ODyG!@P&MLMB71A~CH1b4vh6IpTvYQ)3f3a5L9U5C%?D5Q&aWFZ;W<;7rq3jEokX z{{$%`{TjIJC>EV{C>pjX8KU)yeQWmQiS$#k#$%MPi=d9M5*4qeP6lx|rSZc!b>Q>e zAZiB*5|J}Uz2b6u^Z@>Jup>P>LiOxTitHLR_Hc`4BDAh9sVhIleYFgtoz-9haOg~v<_zCHdox>sqOKOdGdyA(SgbPDvFp$%qu%-w~V!Qd~V{9Y?}Zq zP!KkkJL7OY@$I6#0qM|G;5|W+G&8@$%bOx@4pyj(; zv(dw>WN*x&4C~vd#z@@Xy};&h!B-+i)jl8>CCx-)c12-8MWVCMVJ1;^`F&5)^G1b- zJI?6jS7vrH4UvfpOZRl3bb5$6w~16(n9-g@-FM5|9-$T`Zx61OifDjao&rhMbxbTr zZK?sJ_k>DOzdtC`HbDt(Z*VUKu&JEDTmVJircR-j{!~H2b+!ee>MvTz#1to~m^;im zXe_i7R-=(W!m3bi8{1?dT5oQwq`8cy0nGrg9W+jL&oRe#`X&;PpblON^?e3c1Bo=y zcY;H1ZL4Wd-ZLai%pI|bqvs$8i;Kh|7C;q~G}HVrFtQe5J6!xZoH`ABt6fdBejN6m z2i^Gcm!L>-pjmCC+IZCyTNd)AwJkRL=rU`so|82wbg0Z2T_FS|{42BhJtpoMjIeE1 zm8^t4p{Sxr#Qh5kq=}bhRv1kS!yxBFjNEw)Q@vA0Q}93e%QYR0*M}oF1L}BhPTFCb z<^n-U?H|l5Oqp#?6=)%t5$chz_KT#sfbtQ!wWKD^E6aF*D4jq3Z?0$L%af=)y{D>* zAn9ZAoKn5^%i{^DY8#JB<@ZcI`Q;>$&qv{7%-dMHS8^=4Eic;4_zgN+hj0CI>s#mW znp_FZC;89DJQdIcNAl8vTnMXfs(@t&%-fz+Gvw8b9MB_m~&w?-r>GV z_#wFN`ptqu5jiS7xj6L!Tz&HD>WB9pz$xU2>vJ12R*V2LJ5w8M{ZC#00o;!e^) zAro+IRL`)?&-tXc%Ft2not^)OhX_-5(i&Co8vK2LK55jg5F0#N6nnUH4tLVXj0HA>?;YQx;2)K5)Ns7{;0P<-Y9f_BB>{RwYqWd$X zd<12SAL;Y%Ip#;IW0wYfP?t%tdUrqcm&>AlE`$WtvcDrS`vqVE%8oGX$6Wm-VDDHJ zMK5n=4i_RZJp09qGn<}KHzP%JcIW;A0I)NyB&0Tk+^=G2<(gYWSP{q z;G}pyKNG_qD&dI{_|dA4t~&}**@)AtRov3ixr@tB!}frLEP)m;bF-IUMfX#roB)>h z{<850a-_C7a!{|j6hVD`2{%0D*7@aOLw!4;#@%X^IRVXZ6UtZ~_g&I3w0s8Qv?$DX z7|;3kWd!?E+Y7o?12;}4`v)4!9eAd(LoJB2vK=_AWK_uM^;fU%Zc}nN7eaQ$G{Ec1O@0=$ok+nj&wV-J30(V;5dm!!VbZ z;$z=02fLoaz=_+B^DiB;(&z>B=wKu!IKDK!SXL3QB5lSW7Q+@&mH83W{B}nVeEtP4 z5c8Z4&uo+5&BN-0X+dAk9`U0}`nH*2g5-1Z8!^-j&$lA~D z?)a-W<^kS$Y2ZyN5GgMqjXBQW6;@Gb;b$NB^tJQx3lD+C*K#pW1XVE zTmUu0;z*K*ve~Kc_$oeA`e&fHAfMOvF6VGC6IlSEg2)E*!Pw7&ol|cCWCjYuo>G<@ zLI5-H5t>pu$;zGhE#Yh-qN^~9r~Obd=4WWosi9v<578z`;6cR@2t1@JWRrtak%7OyBlu+ zSOt;!e9F%t-#r)13umh~0PN~4@xg~WU;EYjv!q@kl7h9|s3k@3oqhgV7cQEPD88Y? z$`64^*PVhx1rXT+!FuzQn8(eI_-o?YS+m819Pnohp8o0O4o}Z&=-4yE>#0yLf)J|n z;s204UPZQqB!@p{hiB1C0O)hZE)V;5yd_@W%OH$Krl4AS%00_2qCJ5U4tiz)KN#!V z-eJ$4aOI@5U!U$3_O1QJVV8>^{bJ|PzFLR3Ut1kPN=2z{kCiygln}VQw=tN0_w)NXHiKm*Cpj_Aqe=3Bz z8>yZu+tzbV4+Vt(GplFZdH)G~*$6jWud|A3M7Ib2iTVZ9G^`aZfvG5ZN_3!WDDK%| ziw|Tc4?UoALcwwyesBvV8_#id0V(8pCL%dosKsGj2 znE`>(%GL+^y6$i#k$NLB>GtnJ;VpV@o5YUPYvLMK-wB9p2@9cm=Ik8;Geub%C~`q* zU{5t^j~4n?e{Ee(k2%oVF6q>HhymC@JrO9*tau=xg1R}ywbU?@l$wNpj4DV!OXZTS z$nD^|k`~FL(pV!Ppbbw@!TPfwO*GiV&ypmJ;BuY~k&1iNbs$eF|7RT$^X6(K1}O63 zSQ{KaY+YnRQ_v1SoYYi$5nw%K$1EQ2(=I{Ov==yKA=nHM2jj{Vb|cgo1{Qt#Ryz-I zNaKWxqK?Qg_a$-r!3j3`A9>~}@-rTBIZ;H0=6kJoLEg<9qTQ69S>2QQ@17tHpmgaJJV4(KO2D6rp5YkQ zQ5{qTkj@PLlQ8Xt0r?X|_(?ILxa_fLPRB^z8F z9ceKg1Ur5)nd#{ORyV6hPZ;hkGqZU~H)g@$mQP;C)=AG8qwb1AweZe~wQ zj7p#!eP00dfX9NcAD$R;NzB-HwerixV?WAsmILaZX@49{gA&W~tI0mK+j0zg8J0id{7>#>m?1=UE5`b#&_XVJni6K4qBR8-~+P^#wStp=@ zyUyaxsv-QKAwRvppv1XKo`^#RKec`NG6;Y2(WDOCku?vYCBl#jF82zKNe^mSY(}we zFY&~=46E{r8q(y#5-^gF;hK5v*R0ft2TLKJd{AcKZR|DivfZld0wFUMV}iDb#t zL4D&lWoHFtX?`khdE)%35U$gzJ07U4P85KB4liR|Igr`;k~kr0)Ekp9RE|>36aJhY z?c_ABQ20wwr1{L-bivFSmVo&Eolid)_U34G0C3t??C~zCi{=JNUO#Z zpgHB_prZ0)FKF-ESg?^P*8tYi$C&j0xp3W~T9BYB&cT#Nu6nNtCx9Yz#!Te+Vx{RWmF+ zXNp5xCrlt4{0ZJ<#nyTl+#^{H84XCa>1}^%Ou%e{aAuO{J?vGJXw2Z+<90&3qw34zl z-(5{-JCD)$gsBlArv$gY+%%F(PZbr7t_S-$vpPAA-p$3o?-yJDD=YsB#V?is*jXr{ z#N}Z6Rp{Qe-db{FQyoP`U}QFKHM6sQ;?>M0CC=XMF-@QouS%332cVWx7Zj=`rQmk>ru(zGh}7OlVs=g%73ov zkgt+0|2oJhXZ|Y+xC;Qm0{3=4`SMiCbla!3k7IStQ;Wfhq)SOaMjT&Z-gA!5pzXO~ z-9}Tup*`XRqit0cbx5t}jc*3|_3X{M2$pCbOXVnVttV z*1Ow&No@zJh~!{9+;U_Lo0L_B=tbYvR>$T6S+={mh(kMy&ZTf8el*bfG@U<(-)7{? zLfiI&i1xYMKFz{&r%ai>0?&l8DHmPol=xsjc(&g<^86yMcVq^EgqrwCy^z1g#R)yEX_v9}nFmU$MKBx3*oz+efq=#Hy7o_*ofI&3d}zUv=#bX{e}O$KD63kaBLXdDY0o zU{6Wa-`)y+MxchM$9MVH{{mFjC7^NMDra@ufr=f^jyY@*fk`dc>T!5Wat#KJf8s_c z@43#W(&xmgx9UX|JsB?j%;^d1TCfu!D@I7b-STAJwBbWyhg1Q;2KKX7Mg!@X?f*Pv zaNyF{PMk8Ayw(e)g@UJn^2YMro%wFSJM{gEj}q5`2kIFjva1;`jZ$^2pu83eBLIYY z4jfOSiMeLuvMo3g^C0W?zd!>&nLi1sK$oC@oZ+WJtHj8+$2ASs_L%^X>hHQf&~Z)& zl+lCIpub9E)lh5Meh)|RS3aExW)5AENm(Q zq87X-e_&~@o{j!D$vKa2Z<0V6B}}jDRxi{1ELtyMNuqoi+h7ZsaXn4E)>~?sM+qCA7lZ-fEZyzTf_dMsXI3q)oK2Qg#i(zXE#h>cF#1?+k48lJApUsHnWBSxE%tFf`Gro5Z8a@Y^${1C$5u*J zXy|O?I;X1 z^kAu*eObj{Am#rg+i!OFWpKqkrq6~Ry5~kV?+Xz(gfG>-o*g2!&dkS1S${q4AQs6` zUr(IbyVR-m78@bc#)D;Mxa{6q*-8J(&Tu1NE70G3>lX;_n#<4jsvm^@T&I4>>IBG{ z4pBmk`G|DY^S(XzBCz9;Mc#MO_5CEYxI4*zptS+~ft1VJ;T4~n%kWv9@J5Q00$YA& z#i#ry>@gUSE)RC>hj_A}@at=Cx1hnRLX%5^*}kc{ROUZ^b}Oihz0Ehq>!`6*JBNN- zu{BSq@kjwhHJIJWOdKMo+#U$+YRAjEnuoS_0wo`4DJV$H{~$TGKjz{JqOZDGRq=6E zNT0^nB~V%MI*p$$kx-j)*nN7PEds>Km7mRL;J2kkjUZUc)(whxScdRj;RsDpqMh@PAr(z%@b? z`Q=mBonv`HlhJ_48=NdEPznZ00WeyYXj?m9iAXUzK1KUyYjw_m+LhP!J`$V)OnIoB z@XMD+uWJ#p<8)fX3H`qewHb>GW)Boxb+DcXA?}4u1sz62spY$3-+1(^*{L&cKqC#* znZN2}c1vJrEYZ#00W3^^=UNQnJ13vlMpP`JvT#;fKGrqx4oS^MP47#H=4e&`%=e)X10On^xp%`CfGp43UBpAzSzsTPrY<*AzKD9Sy ze`{OSmBts0C=dYCU}vgi6(80GLy{z%;jmAUwxu2Ci-Bpjr8ADPFY(@WEW-wMuzm-Ybt^>94mpKzoDd@h5#-8u+7gFVVrDAl9bbB zB8VSS~Zn?Z_@)&HG0VZR?O+#6%D;G=%KyDA%g8bF!O1kB zz*spQT4?s9!eEP*%g<-mY3c5HSL#L zcFH+eN%(mfYLVG#o6~Y2DYa9jx>*ZQ8~3*(-=zGd0QhLZB{VUlt8YN#3YC80nd|f& zr?#6^-Q_wbg|Tl|VH?7)kXdrG%_^XHqk-aWb3mC_z)XWd!e2y+Dt;x@^Y|O>jmAVC znJawe`!&jYb}HSkYSlORAfwi@SOWbUsF(M320K1+cw(nHWgfJ@9a0D&nq`!m4UFGxT)djcz`Ip9 z-ol4nM>L&P>I)*$QIY49W6yLRC|XwTi3Bx6<-Kp|{~d|ga8+V@+V7);${qM%XaA>< zBYQ0BX!QdQ6swJ-q>3%(peHgrmu&_z@KZJJ|=-d57od{sI3 zZ&x!**KdFBEL+vVxC$^3D9Ld0tTE6en9F_T;sH(Ep)^odA1+MK&!6kHZ^9Gfug?6p z8Awt>M6w(YV!){DF)YAo9F5P_X4VZHMUfSQ=WI^(Ce&^W9G1y0lTdjl!;`KK@UwLo z2-uJwk=6$4PM`ZzFTV<>iPm0_DtLGAL)J+xcwwTV`?98wkCB*eHJG1*7PGmLM51B+ zfK+Lpvv49zzu8RuvH+l+qRyF!DIc%FYd3&O^3@(Yt}ik#HfT**dcJ4-98pe;sEKNH zJ3I10J`Y&3)HbA?dr0gw8F){>Qvy5CfJ*9t4jFz<5NI6i5I+`kS5a)w%v3jvoVf+Z z>p?lZ!7673lu;zmE@ozkv6nXiRnA3(xV%F$23SwD8BSG%5Y48 zR%t!v6s&g|Z;}Ij(TO7@?8wGrxs%x=0$?(dF9q>!v|9^4j|B~M4EQty&=f2hXTfaC zOB@(R3YP4>ngc`@*E5G!`ZV7ZderS%dX9VKt*eO2iUAE~xQe3WT4*aTcLFcGX!jpy z7=iebb9DFwq$5;Wh>u;&SsJYeA2o(sto{d%ni`{@_O^cSI`0vRr4br<=S$lw> zEJM4#n{N}sCZD`p1c)qbK`7g&B&D% z25Pk8XNVv;Z+e3)biW?+an7LXgX(!@{H=!o)VJ6lnF{O}OHWfXzLCEz8!?!RV4ygd z(5;(m@j3M*8P+P|*K~y{PXxsHEu~?6Z-M`7M#_KH(*|9lWA}Oy$QYkQS|A>=U+5*s z^=EI(KKJ7fALOX?5jzG-zduMBV2o&f4#mz1dNntyqA)EEM|054G%!y@?i#Jwj~)aw zO!A)7kkGg6@j?2Aj|v#GhBSa)+xJ6yV+rf0bp*q3j-!DwXG?6`wbRzBFdn55)&2`hsj9Q{$@%iG<{j>SqVeOJWH6)04EoC}<05L?DPSxbbFj7V zDCrU)y(HjErwiZEKnoOS(4A8*y}K+vebh#vfm72OX;lCVdA;MsMza5}<1f$2cEQ^E z&>LJ`hSV%*AQVioV^kG2#A6?U>l%s(u9pm6@vIrHXtfGDQefoxJ*>C=}0o z^a*mD+wlJuwRdy@BKAg)ydnC7@j0+480_+%Z%#U^S`X$4$2c?j8TkJcEm3WUG+#!z z-+3B?v36pAtJJ1Y<>G7uRL+d<(5P;^DTfE z1TI7*6P?Er>_x@6N55`^$qm}}x+2=u^Zu9BmLOp-8?3v>Xvv^G<8jKtU`na`Aa~MC zNYo8Toxm1gbWnQBBC&sJx^Ky)LDok+S5Y8B-gE9h9hA0OqPVWvODD{dG~3MH<*x!n zxP*j1lTPv)xQD!MYbC-k|Fs!Q6K{x;!LcW5>h7?8l>}1T5t+lywvIqU^4675F5bTJ zsoo4y<_9o$y9VBCw6NXjA%g0>lonJyoj2zu3=1+1kl-4829Z1k(`BHqN2(~67>xj- z0j}=~l)Sw0JBFo&^?e(J=b*3vZxb!4N3k$`L}fRa=C`tHCW8T?60jbTEY_ykD?c&d zD={WnQsrHFWfDmzcJ6N9%1@@N6{IFI@BjfwX|_r};#Nd_fSk3eGnaTI;Z)@(<9|+< z*fmwI$D254-3E^M2B9UtGqbhk>R;yPQoSy3hBT*v2@K#RmNgIF{mb_qOhm{Oa&5#$ zYg=SaXpMrVPJ>}wU>L%YbM~5!w?d2uT=s)+>Lu~4b34A1%orbX z858rSABMH|$1xKY{Ls2HT?&Dw-Q#T`!mqyX8fh|Y(2nKAX@IYcqGV6C$Qt6kPl7D{D?rfto;sZejz+gOWl6` zRhqCL7ZA`vF?+3j2DA^S<+POXhC183nb);;47a0TXX)jDXd3Q}q_VNKgZoK{GxV4@ z4;aTSSED%vwo%*595iWh%DC#DPZ%dgs^OOYFj(LQ&0b^}%C?<(&5Q*yXDN7@<`ex0 zU=E%Ag;Cw8^HlwH_F;}eK6xn1{V)CiwCLBm7pEGMHGS=*K!j`%+JOFPR)#@(Yak_O zrfp2?c}>oVN5(SF23}Lb8&r+I$;pRm2bTRwGY9(~<6e&P7Row_nUVc_>8TYnsc!)0 z-9n}KG3L^=a`p17%wMNIB@4>E8kijL=Avk80_}pMV_{@O)+8>%wL2ar&(5Wy0!<6D zM*h4cXd!AXYOQ>B0|I%a%I%YMd_N*Naz6 z{>9CEtV_sA1AFqU?=8{Z>hHJM?5g#iy+)PC=ilj!>ESv$SMWkd8Zes3 zTd13qZ+z>%cU3#Q<687uphz--`s{UZ!gg`e^rKzj^IfHinSiAHYPz|BK%kNAj=w4o z2JJq46|yXor?<_?-*sP=<)Y26rV@^La(riBwose{u-=+qo`N{u1+h?mbvZrMJHQnLHww?P(flH^ zPpwU;niTyM6x5vodOS7G^&&E9?(1AZ@@@;aDl}vJwd_G?t=@sZne$Pq0*PXFr0Y&g zaUtRsyNP0k8zh+pj;JUfPAfH{ya6maUD%_Hy;$sL`1G$AAGtgVBy4xQm4@FcG*1BD z!Ri|keb}ZR1X3jQZUq?i7MHY%);@O;)Z3CGV7^FcDB^KuMKtOmbl6(DHZ;%K=Vo&{ z>iHm}q3{`{5a*TwB}L*7nD(zCph1)9nSGA1ZeTPm5aj{gh8g5rap zj!iYn%`>hWkSl{mHrBDJaoJYCMQ22c#5v!(F8OG~C#pO#{wUCQtOugL|Z z{PNU3_^4;si9P4}N_|)AOzR^)VRV28-TIf&^DwQ4#3UajpuG)>3c&V`p7(CHFZgq| z83yJ-A3^s?x>CH28Df+Zrv}`A=ua4;)*$4`Fr&;D`8+t}t{_T(>7l?e%x z(Yz%EK)%DzIU$}*r|3!2N?cZ+d9j(`ukf=W&WB%yH79Jo{h>j5tf zIVNVF2sWSC4^FQ5Ym#hu(NmHJjt!>3xQnZ|mO0Myme1E|bn1!}z0lDHfdcyHH&OK% zaM_z7rI{6xX2$S0+3OWRF*liSQbF-Lq{SA1f>!@5{s!uOP4<6nyOdr&!Yk`61eh@+ z(BxS8r`v<%;}0_bx^z7G584g?!{m+BC_~+YxC_KJRQum85|aXU8kO| zT`>s{*DH52;NO2lW#^i6b~>WRPq909T+Ve@C>Zb2-_Iq%AHA~(Le=>1nSNK3BTrTk z`YH2~m@ms#oZbm<@01M6*j}d{kXDhZu2NLmxy#OT43^@N-e<>SsD;{m8ndyn-|*$0 zXX%LUdl(x-mb&!y8*1k~;{>%_RTPT`s^?~Sw(l^z+Ld?O`h6w^x&?Mg?znZa3WF*f zo7-?$G=>^}8Qv}HlV4!_(h)*wrNXc_oW=K2`=O;lFzx=YN)*PTr~(YZi^_=t)#u@~ z<;J3KiX)KsZomqU43EP3Y7A;I1{xVHH+a%pUV+VE)Ysu)bTxb3?OU^Mmhty}~G zqZDe=XRf%uE!%flMK(lg`*hzqo~sOlz1JE065E&sYO`?e5+TizP<6o0|9Un4Q%Rr=_g8gat$K^I+*!CH!Z7nh}oq-;PAQ(2U{V_8R!csZ%kPTVrZdC!J-?jJ{# zPzE>F1!2>^y8ZjW!1S&j5ty_)UU=@5=|Dj}zrgb4=x_Z+q!Sz$EddlF%m#V zQzAzLzhDF%VlTM7JE~GI`UZ*OUygAK_KY*Go{xci*mYY;%-Vbm{$o zTzlEJ^S6w_YjZ^dCF56B+k|4BJI3yG|3!Y@UHN+SRDlR$eP@^VvXdp%&ANenrhIJf z1C6RFS74c1F#bA6YT}}Ut<#dXY!Ta9>p8(g3n5jt^SoVb{!9<*RkiNOPzYGxx`!bzxkBWy;!N4THTEzZAu5D1AnkR#uUsI!Hu__ zzuf!yFR9zG&%0%dOb95Ml}#m{V7|8m&y|@kWgGJ_p=Y>z%Yw9_t*bei6$V@oxEOvj z+SpUG*DNeKh{ttJxp&!6m^shb9BDQG3yP*(=PU$k| z>$n}Lk%^;?Q{_F42;jQKGzs`)!^qHo`rU}1{;}cMSg0fDPUdk(lbXiltpdze3VL2pZpsOsgG_Ew*>^TGB@j@P5K9)OL>+;RDjh=#|61A4F<_#l$pn zI*fUQ&R{=$i@|5ayGIt^4+uJ-=^WG-0%HEVK7q}Pz46owj%(5S{-E!%xvpjct?t^@ zp))r0A0&^iBa`-7458=E@v=jHO&bM5V%(SS(*6I@KfsDb9HpGm!s(R>%RsAUU{f#3 z7qi%J1a~Rx!i5kjd zP@qQ9)LHo0k!#>aRnyXQ?Q{1|qLwaz+>2lxk?td{#0Sykk8-X{5#|7qXmUD$9rw=jZ@~bXGf=PH4TR@ac=e1eH_Df}>77Ru_h7 z>Fi!h12y@Ieggp^`bh1kb_EX9dGI3aI({zE6{epIygf$ElS_Ql0{FY4^#ZO_2(O-E zP6>{_?(eNI^lzVA_F?Bty7F?U1#U44tjXpeysO$;>@TN;*M66#oG(Sp>T(Zd(rA#zY%< z5e{U|Y6*X-dHV++GY9)ftU?wIAU>C`=ZwQu&Q(9}1;l_oYD-+$?sKYyQY(`*RlNyyvI`nHrD-Z7YIcQhv1qIzrtT zQXp~*t*1W*pt6U~T-PT?4nX%8y=rxw+Fo+NCqB%6cN{X#k>K&KfIxj;HZhiMYtxSh zF63ggk&^a>E8&m<0xyZY46xwTe_1--^hcHsgvY)jN9k`^>MkqQxZSm(1GQ22pL z{Y8%bl^O>8kekFLR=LF#->(N;R@yC*UDKJ^lZ`mEzTnc1fr00~rhSCisQg>eL*;vv z`Sw`sTmtUO4xzZWQcG#`)imJsTcDH1L@$@*&vP9btMMM23~f=Z=ctn#8P{)|CB)a# zT690GkfhHJy}lj=UR&L4$$H;r#3mJD($^C!==&ZB@8N*!R3+}O{9E9F>Szs6jukITeg|>O>WK)P8+*?LG)r@ z?4(8E&^M8M5C~b*Q`wmn%-Mz@vsT1=QT6tmbF9A)WV+1v9y18--M;|!bxBX6mQ1S8 zXzv@%hK?nNB$t)P=novVC`TXbyomf~fMZ;KVCQk0az}qGfJ&Bj`>y83MVY=mkAd-& z7XuI5n%4)J^K(U1j$R+A3SZMI^_F*K#ex%gtp9oJ7w)1J=^sYzkjxPYfpOU8Yjt^e~TXK>@-la#Apu7xlU~_dwEbEjvzOUl=7v_p<`7+$0FGSRp6NJc~wPI zkK*?Z3%tcM26l3PZ9KN;U|wRilZIJe`MD5Dx#CV3B~7t=TXS>8warW?bECJjYl7(4 zlf0ZdKJ#+LkBoigsE4_0P&-$)K05+4^UzSD19r87~~@>23vcT8qprRzkq@!32wu3NT>6s*x& z!6fH0WL9m3^EXM6_Hll`8@jq@{0{1`LgIwi5E95>RmGxChsV5Y=V@+ZhJANC?HX!3 z9T`dpP_}kZa(recPGof0K+X0tadkY_aQ8qoZTpK4>-WPpmC1k?mvvM)h^vEz&k2Bq zy?k_sx`wazR6&)G>fpEZTnXE9z!$!x;t!RJtX0f9h$j_`{DM59`$IdZy`HY~#{BD%1Vq7p|tfvC)<%Q{-@w*e>EheV=p}>EOU7R@Bx8|wZ@2%eDup8YthSw>^3K^oL|4f`Z& zLB$ZW#@ABvt*YxDgAf-n-gveF>01Bq@^5&gGn0KDbUZklf?hmR)92fyKa#T$?`;v{ zg@1oitX?{wQ!ISXMc}uBS=}%$sFVeL!7OzY|2k2JIz~OtY=7tL-sicuRSL)m%F;`Wbx>A3k++iA75#@_V zh8}HFgyKvv2gI3m^{y&lW~oew=+8uF*i0J36gj{YgZ73;HJ$xK&cQwl%Zbr1&wr12 zBabrvcDke{B}Uu7B5Ra&T3i3uz0ZK2vH=AM-XIU2?+fK>HFp`R>8jg&R{Pl!qhEve zuy1#iI~Os+QrE09^6ox(&l`H9SL%9py#1&)E(i-4!D0Hy`D$G@W?_HV?yr2Dnu8&@ zE3gU)>}f`Y{VozZ#YEqbVL=9Ys!h?`%QATOV0==n_d<{oNAmLnW-ODQ9%rjyenGA5N6xDTWLB6A|>&+8Qz#D z-zCg@1onvR?QS=z0%j zB_7YHdx<&&Y^wfTl?KCg`#2o^IqZ6lVaJyvS**ePD`y1O+elu#x8vos;8r5iZs1?+ zDjvORk(cc0MF?^rIw=}1^_LL7OB^`ll%e-_j`m!$-!$zuWJIOl+xR-tJj%YdT?p_u z=Y>-I8DE2QkOb%Z&}@) z=vd+2zFIM23mS*)KD4oyaCCbni8IY81!S{)`uoPPh3AqAng(OeJ-O^RdO&5FpRD6> z)`oc9!Wo><7CNB`m$1-Wy;E14^ttu;LDovYeQM^>^{;FAVcjncNA?{%b|8u+6E{92 z^?dCfokpi&KGurRyCb|uX<-H9E0VjbJo1GFgr*ymDFT=iY(S{=LKJ4sFh+qI3&K}5qkGilt) z#+=Y@oc=>#rDrLh4E3oeB`mV>)zvzMp^Pl*ys$DqL-npiSx#ybm>`DiET*=zANq}# zytF!M-AZbW&+|688Bn%6T3A;m{XA#!9kEbvvBV+NBjpZILVZ_1+*4Q}C4lyk8y^UG z*~Cq-)HexkY_t9-)%5!dIOS+&h0j$V?kzqzO+-kV?Vmz$ItyzDAF;uJxKmcSc%R|o z-Gv3%-b`fs6~7twNZr^WPZTGLi-#HAeJK!Bxewh898SdjHTcXn?R625f~j$lE*10< z$*^rqb6#Atf4xMRi`ivJewVWHib{Y-TU0UG#>i-jw=HvYEkRZu$M=6H!3(2wz%4BNiY+TgF$HR(L`m8C+>=2UvAU*F(T`+LWZJ4TtiDoI zzfn6~!n{om9!i(P8(MT|*bCrfc%d{sM5vfd8~wiT->%;97NqbABYd2(c!r!ypf2s-@$ zV+~#R$zEQZS+wtDwe$0Tjxw`lzOsksYOrH6)$keh=sZoRyP@%2yD|-r6^ASGWQom- zeXp`;PI9K!>W)u_A^$TQ;$u!Zbs>nmhcJ`oV?CZhY_8@X-Z5?V#?4@p2alyPi>%VC zS$Pqf;toc4l)tA4GTpq6D)l@af(=Ec+oZ2(<5NPCt2g9BtF0KjTR<#*rr6n@VYUTs<+I8WgP4I?-xEFPahM}V`m`4 zdRKz4Fq-gp8aQF>dF}&Q>if(j3nkBeWaFKXGJ3@mDq>?fzNGXIT7&A+#vL|NsofHp zI(S~HJ9jUM)rV3YpxmdAtX4e?Qm~WCYW(C)20xWpT9`{->?!K1IlXA=BG`FuK0hX& zS!iU$4sTi-Cljd^rmU;g#=IfKl2f?LcbsvFA$~#Vvac5sbDI4RFS!aL7=#G1*z`l$ z;puB0-!e3PIOu2HQq|mQeQjlDkV!P&dJik#Z9-RD*f`R4zgO8Xd)9{(pvy|N;H$0ce;;ZRO_7I(37@7Rb7$&DKz_lnuU{9;dIW1V*v&{krJsp-|Lh*CAm)SA4PP zHGiIy>bpG_FQfZnF6#WF@ z5N%vKTVpa+zEJW=@QmJ$6na2VY&+MS1X<$T5Ntzo9qn;_8?w*wu?{(* z{sXpF-A=t4YAwdOb^8*`G$CFKY9*JPf2J$8`n%ZprI_^UZg9a@Nor_1K2a|xp~gQ_ zY-9QC4U#1wFVW|H(zAlZJIBwL9_=N)HBRT~cGZ0fHEjF^RStwqLAr+|8gZ^O-?WBB zK5Q|>LNPnFk`tUtis+;`l{ozhrie}$@;uu4a9i}**F^=!`CgQm*ushYUez@Mc2|9v zd`@zZBI5=c`a914q)g%0(n*gAq!8{AnsdYJcOzVK^Ai{9aQ$anX9PhKinMIIegPmx z_>7mk7-s&_n zcreqJE@`BP@s5a$7$;=|aJBe1(|n3Fo1~Ju9{tp$XoA7Q5Jd_MjkAZ;sc*_9Di#e@ z(PJ0G4dS6;K{(r3${^MDCkYtqppzHs1%k%jib|P&0t_{+CB&|( zCZttReObt_^1h}vnO$Mnz{3yBni1tVwnFi3`)8g)=IOIM>|r6J+p(Is`(FZM$dq-* z8Om^^C?VAC9-)bhRc#r9df(_^{|Q2ncc41zeJ{cA?i*s=DLsRvi#@Iv>!sJkhfrrw zlTbCuE({(MYNaERS9VL|%uK2qWaEg#{TZtH6ZC?d(Z5x3ioJUliz$N>JxH~yP2LQ# z;{zeRPDK?kPV6o83r?E6+6Idksft5}&3BI|XSaD(ctyR6n$xj!Sos_8wk@KNPKi4` zFYlewqi@EOkNZgOXIqA;lAbMvjDyQJRV=+MS?bf`Uj#k|4jc#ifQv%~^HXS#zg~Il z0aPHC`R?%0;lIPvzBFi#!We-<{jSOD+8;;RE{x?vcPh^c4_T`=0?IMrLFtA_PFB0K zTU~jMOYkZCSg-r~vI7`Q_|0T%Spcjh+%h4{v{(H3ssASC-n*iM6r40q z#Gb4D5?0eDIU)t`wXTbBd0~NVK={BJC?LL@)|o;qQeZ!xRr|yK@_`K}P?KW1|Lih* zI@n<@q3uxBT`X;-b3nBF)b9TKr98jH@-2Ib%qSMo07~^u_9Gd$%r~5E;z4?XIQKS1 zQ?Ly;Xlph`wjJ*DBlwzgyL{^P@di+7ifN|?jRMmY*0Q-%PfP6f4pnX}T)ynQimBjI z>B72Mh+Y&La6wU8#=tAMTe0z==xV(d`HbBFp4H&bVw_b*j%9bfZSmPahK>xH<3-uV z>&>IbPJlLW%vwZ)HW`E6O=!+@n~;w`XErn_UyN)wUAP@J+g*LjR4g7$S-xx;thm7k zcdj?MC6kkSCqg&Ahpy&|w=d23nlt@g1Sh*lJilfwoKdR?v|1mcp=CvAUKaOC-pu6! zs^Ts~Od^dyRG#e!d4hRqk~o61pec0D`L+`aQjdPObL#tyMfeQD>$DDv_qs%n&Bn%DjKlF!KV z1rC1U(JYm;`fT*nkA`bLhTx3 zr=MUPiJ}q6?MmsOqEU6D2`R|fFYf}0sqDD$INiKs3s@u58E)e^AE*G*XhJTNiz7`< zUH4KkTa4Lly9Tgv;&si%zC+d#z1>6UcCfg}6XoL<6Ir_Q{#cdBTAIEBlGq~Tx_q!r zHse7=KQWm*gWD=voE%t(yF?LcU8mB6r1>0KT#}gi0(n1j2Z+k4kH}x&cyrawK>hdm z+aO(w9Aisr&r2pn{;?aJ06Cr`!pd;$Lv9*5G^fzm0^C637n&FWH&8<`0t*_v(AWap z;P;6|qbfQAA99MB|0 zO+wTpL`_1}Bt%U@)FebrLR2H6h6mR0z~BhKe;)XMw)&Nb=E;O{LHYaZFi*x&_D7>m z`b7oU_(ul7uVof!3(QWmIsCYrTiRG!+gM@D&}bVpx??z6??1i}8h$c3F!sNHq3%N0 Rj|e0juy_8d?91bce+RWJs)qmo literal 0 HcmV?d00001 diff --git a/corpus/templates/static/img/electrika/first_prize.svg b/corpus/templates/static/img/electrika/first_prize.svg new file mode 100644 index 00000000..b280828d --- /dev/null +++ b/corpus/templates/static/img/electrika/first_prize.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/corpus/templates/static/img/electrika/logo.svg b/corpus/templates/static/img/electrika/logo.svg new file mode 100644 index 00000000..03fe3020 --- /dev/null +++ b/corpus/templates/static/img/electrika/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/corpus/templates/static/img/electrika/second_prize.svg b/corpus/templates/static/img/electrika/second_prize.svg new file mode 100644 index 00000000..5f2ce27d --- /dev/null +++ b/corpus/templates/static/img/electrika/second_prize.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/corpus/templates/static/js/electrika/particles.json b/corpus/templates/static/js/electrika/particles.json new file mode 100644 index 00000000..3e0f4ced --- /dev/null +++ b/corpus/templates/static/js/electrika/particles.json @@ -0,0 +1,110 @@ +{ + "particles": { + "number": { + "value": 160, + "density": { + "enable": true, + "value_area": 800 + } + }, + "color": { + "value": "#03e9f4" + }, + "shape": { + "type": "circle", + "stroke": { + "width": 0, + "color": "#000000" + }, + "polygon": { + "nb_sides": 5 + }, + "image": { + "src": "img/github.svg", + "width": 100, + "height": 100 + } + }, + "opacity": { + "value": 1, + "random": true, + "anim": { + "enable": true, + "speed": 1, + "opacity_min": 0, + "sync": false + } + }, + "size": { + "value": 3, + "random": true, + "anim": { + "enable": false, + "speed": 4, + "size_min": 0.3, + "sync": false + } + }, + "line_linked": { + "enable": false, + "distance": 150, + "color": "#ffffff", + "opacity": 0.4, + "width": 1 + }, + "move": { + "enable": true, + "speed": 1, + "direction": "none", + "random": true, + "straight": false, + "out_mode": "out", + "bounce": false, + "attract": { + "enable": false, + "rotateX": 600, + "rotateY": 600 + } + } + }, + "interactivity": { + "detect_on": "canvas", + "events": { + "onhover": { + "enable": false, + "mode": "bubble" + }, + "onclick": { + "enable": false, + "mode": "repulse" + }, + "resize": false + }, + "modes": { + "grab": { + "distance": 400, + "line_linked": { + "opacity": 1 + } + }, + "bubble": { + "distance": 250, + "size": 0, + "duration": 2, + "opacity": 0, + "speed": 3 + }, + "repulse": { + "distance": 400, + "duration": 0.4 + }, + "push": { + "particles_nb": 4 + }, + "remove": { + "particles_nb": 2 + } + } + }, + "retina_detect": true +} \ No newline at end of file From 972a97c5ece1deee62697a619b1ff2052be76967 Mon Sep 17 00:00:00 2001 From: Vignaraj-pai Date: Fri, 12 Jan 2024 19:34:18 +0530 Subject: [PATCH 2/3] Refactor Electrika models and URLs --- corpus/electrika/migrations/0001_initial.py | 66 +++++++ corpus/electrika/models.py | 8 +- corpus/electrika/urls.py | 10 -- corpus/electrika/views.py | 119 ++----------- corpus/templates/electrika/admin/admin.html | 18 ++ .../electrika/admin/announcements.html | 163 ++++++++++++++++++ .../templates/electrika/admin/team_page.html | 48 ++++++ corpus/templates/electrika/admin/teams.html | 40 +++++ corpus/templates/electrika/admin/users.html | 58 +++++++ corpus/templates/electrika/home.html | 48 ++++-- corpus/templates/electrika/index.html | 97 +++++------ .../emails/electrika/announcement.html | 17 ++ 12 files changed, 495 insertions(+), 197 deletions(-) create mode 100644 corpus/electrika/migrations/0001_initial.py create mode 100644 corpus/templates/electrika/admin/admin.html create mode 100644 corpus/templates/electrika/admin/announcements.html create mode 100644 corpus/templates/electrika/admin/team_page.html create mode 100644 corpus/templates/electrika/admin/teams.html create mode 100644 corpus/templates/electrika/admin/users.html create mode 100644 corpus/templates/emails/electrika/announcement.html diff --git a/corpus/electrika/migrations/0001_initial.py b/corpus/electrika/migrations/0001_initial.py new file mode 100644 index 00000000..cb5251ad --- /dev/null +++ b/corpus/electrika/migrations/0001_initial.py @@ -0,0 +1,66 @@ +# Generated by Django 4.2.7 on 2024-01-12 14:03 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +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 Electrika Users'), ('T', 'All Electrika Teams'), ('N', 'Registered for Electrika but no team')], default='A', max_length=2)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_modified', models.DateTimeField(auto_now=True)), + ], + ), + migrations.CreateModel( + name='ElectrikaUser', + 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)), + ], + ), + 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)), + ('team_leader', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leader', to='electrika.electrikauser')), + ], + ), + 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='electrika.team')), + ], + ), + migrations.AddField( + model_name='electrikauser', + name='team', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='team', to='electrika.team'), + ), + migrations.AddField( + model_name='electrikauser', + name='user', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/corpus/electrika/models.py b/corpus/electrika/models.py index 78813852..d69882d5 100644 --- a/corpus/electrika/models.py +++ b/corpus/electrika/models.py @@ -23,11 +23,6 @@ class Team(models.Model): team_leader = models.ForeignKey( ElectrikaUser, 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 @@ -35,8 +30,7 @@ class Announcement(models.Model): AnnouncementType = ( ("A", "All Electrika Users"), - ("P", "Paid Teams"), - ("U", "Unpaid Teams"), + ("T", "All Electrika Teams"), ("N", "Registered for Electrika but no team"), ) diff --git a/corpus/electrika/urls.py b/corpus/electrika/urls.py index 6902d3e9..25420341 100644 --- a/corpus/electrika/urls.py +++ b/corpus/electrika/urls.py @@ -16,16 +16,6 @@ path("admin", views.admin, name="electrika_admin"), path("admin/teams", views.team_management, name="electrika_admin_teams"), path("admin/team/", views.team_page, name="electrika_admin_team_page"), - path( - "admin/team//mark_payment_complete", - views.mark_payment_complete, - name="electrika_admin_mark_payment_complete", - ), - path( - "admin/team//mark_payment_incomplete", - views.mark_payment_incomplete, - name="electrika_admin_mark_payment_incomplete", - ), path("admin/users", views.user_management, name="electrika_admin_users"), path( "admins/announcements", diff --git a/corpus/electrika/views.py b/corpus/electrika/views.py index 50c70935..322f40d6 100644 --- a/corpus/electrika/views.py +++ b/corpus/electrika/views.py @@ -40,6 +40,8 @@ def home(request): config["reg_end_datetime"], ) + print(reg_start_datetime, reg_end_datetime) + reg_start_datetime, reg_end_datetime = datetime.strptime( reg_start_datetime, DATETIME_FORMAT ), datetime.strptime(reg_end_datetime, DATETIME_FORMAT) @@ -53,6 +55,8 @@ def home(request): args["registration_active"] = registration_active args["registration_done"] = registration_done + print(args) + return render( request, "electrika/home.html", @@ -68,7 +72,7 @@ def index(request): args["electrika_user"] = electrika_user except ElectrikaUser.DoesNotExist: messages.error(request, "Please register for Electrika first!") - return redirect("electrika_user") + return redirect("electrika_home") if electrika_user.team is not None: args["in_team"] = True @@ -97,14 +101,6 @@ def index(request): 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 @@ -131,14 +127,9 @@ def index(request): 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: + if electrika_user.team is not None: + announcements = Announcement.objects.filter(announcement_type__in=["A", "T"]) + else: announcements = Announcement.objects.filter(announcement_type__in=["A", "N"]) announcements = announcements.order_by("-date_created") @@ -215,11 +206,6 @@ def create_team(request): electrika_user = ElectrikaUser.objects.get(user=request.user) team.team_leader = electrika_user - if electrika_user.from_nitk or electrika_user.ieee_member: - team.payment_status = "E" - else: - team.payment_status = "U" - team.save() electrika_user.team = team electrika_user.save() @@ -306,11 +292,6 @@ def accept_invite(request, pk): electrika_user.team = invite.inviting_team electrika_user.save() - if electrika_user.from_nitk or electrika_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!") @@ -371,59 +352,25 @@ def announcements_management(request): 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="P").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( ElectrikaUser.objects.values_list("user__email", flat=True) ) - elif announcement.announcement_type == "P": - email_ids = list( - ElectrikaUser.objects.filter(team__payment_status="P").values_list( - "user__email", flat=True - ) - ) - elif announcement.announcement_type == "U": + elif announcement.announcement_type == "N": email_ids = list( - ElectrikaUser.objects.filter(team__payment_status="U").values_list( + ElectrikaUser.objects.filter(team=None).values_list( "user__email", flat=True ) ) - elif announcement.announcement_type == "N": + elif announcement.announcement_type == "T": email_ids = list( - ElectrikaUser.objects.filter(team=None).values_list( + ElectrikaUser.objects.filter(team__isnull=False).values_list( "user__email", flat=True ) ) - elif announcement.announcement_type == "NI": - # all users who have not registered for electrika - users = User.objects.exclude( - email__in=ElectrikaUser.objects.values_list("user__email", flat=True) - ) - - users = users.exclude( - email__in=[ - "electrika_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( @@ -453,40 +400,6 @@ def delete_announcement(request, pk): messages.success(request, "Successfully deleted announcement!") return redirect("electrika_announcements") -@login_required -@ensure_group_membership(group_names=["electrika_admin"]) -def mark_payment_complete(request, pk): - team = Team.objects.get(pk=pk) - team.payment_status = "P" - team.save() - for member in ElectrikaUser.objects.filter(team=team): - if member.user.email != None: - send_email( - "Payment Complete | Electrika", - "emails/electrika/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("electrika_admin_team_page", pk=pk) - -@login_required -@ensure_group_membership(group_names=["electrika_admin"]) -def mark_payment_incomplete(request, pk): - team = Team.objects.get(pk=pk) - team.payment_status = "U" - team.save() - for member in ElectrikaUser.objects.filter(team=team): - if member.user.email != None: - send_email( - "Payment Incomplete | Electrika", - "emails/electrika/payment_incomplete.html", - {"team": team, "user": member.user}, - bcc=[member.user.email], - ) - messages.success(request, "Successfully marked payment as incomplete!") - return redirect("electrika_admin_team_page", pk=pk) - @login_required @ensure_group_membership(group_names=["electrika_admin"]) @@ -504,12 +417,6 @@ def download_csv_non_registrants(request): email__in=ElectrikaUser.objects.values_list("user__email", flat=True) ) - users = users.exclude( - email__in=[ - "electrika_admin", - "embedathon_admin", - ] - ) users = users.exclude(is_staff=True) users = users.exclude(is_superuser=True) diff --git a/corpus/templates/electrika/admin/admin.html b/corpus/templates/electrika/admin/admin.html new file mode 100644 index 00000000..9fcc2b62 --- /dev/null +++ b/corpus/templates/electrika/admin/admin.html @@ -0,0 +1,18 @@ +{% extends 'electrika/base.html' %} + +{% block title %} + Admin + {{ block.super }} +{% endblock %} + +{% block content %} + +{% endblock %} \ No newline at end of file diff --git a/corpus/templates/electrika/admin/announcements.html b/corpus/templates/electrika/admin/announcements.html new file mode 100644 index 00000000..0f1f0ec1 --- /dev/null +++ b/corpus/templates/electrika/admin/announcements.html @@ -0,0 +1,163 @@ +{% extends 'electrika/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/electrika/admin/team_page.html b/corpus/templates/electrika/admin/team_page.html new file mode 100644 index 00000000..17b1710d --- /dev/null +++ b/corpus/templates/electrika/admin/team_page.html @@ -0,0 +1,48 @@ +{% extends 'electrika/base.html' %} + +{% load static %} + +{% block title %} + {{ team.team_name }} | Team Management + {{ block.super }} +{% endblock %} + +{% block content %} +
+

{{ team.team_name }}

+
+

Members

+
+ + + + + + + + + + + + + + {% for member in members %} + + + + + + + + + + {% endfor %} + +
NameEmailFrom NITK?College NameIEEE Member?IEEE Membership Number
{{ forloop.counter }}{{ member.user }}{{ member.user.email }} {{ member.from_nitk|yesno:"Yes,No" }}{{ member.college_name }}{{ member.ieee_member|yesno:"Yes,No" }}{{ member.ieee_membership_no }}
+
+
+ + Back +
+{% endblock %} diff --git a/corpus/templates/electrika/admin/teams.html b/corpus/templates/electrika/admin/teams.html new file mode 100644 index 00000000..8ab550af --- /dev/null +++ b/corpus/templates/electrika/admin/teams.html @@ -0,0 +1,40 @@ +{% extends 'electrika/base.html' %} + +{% block title %} + Team Management + {{ block.super }} +{% endblock %} + +{% block content %} +
+

Team Management

+

Number of teams: {{ teams.count }}

+
+ + + + + + + + + + + {% for team in teams %} + + + + + + + {% endfor %} + +
NameLeaderActions
{{ forloop.counter }}{{ team.team_name }}{{ team.team_leader.user }} + View +
+
+ + Back +
+{% endblock %} diff --git a/corpus/templates/electrika/admin/users.html b/corpus/templates/electrika/admin/users.html new file mode 100644 index 00000000..3732a1b2 --- /dev/null +++ b/corpus/templates/electrika/admin/users.html @@ -0,0 +1,58 @@ +{% 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 %} + +
NameEmailFrom NITK?College NameIEEE Member?IEEE Membership NumberIn a Team?
{{ forloop.counter }}{{ user.user }}{{ user.user.email }}{{ 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/electrika/home.html b/corpus/templates/electrika/home.html index 1843709b..1ccbf977 100644 --- a/corpus/templates/electrika/home.html +++ b/corpus/templates/electrika/home.html @@ -77,27 +77,37 @@

Admin + Admin {% endif %}

POWER ELECTRONICS HACKATHON

{% comment %}

Theme: Explainable AI

{% endcomment %}

23rd January 2024 - 3rd Feb 2024

- {% if registration_active and not registration_done and not registered %} - - - - - - Register Now - - {% elif registration_done and not registered %} - Registerations Closed - {% elif registered %} - Go to Dashboard - {% else %} - Registerations Opening Soon - {% endif %} - + + + + + + + {% if registration_active and not registration_done and not registered %} + Register Now + {% elif registration_done and not registered %} + Registerations Closed + {% elif registered %} + Go to Dashboard + {% else %} + Registerations Opening Soon + {% endif %} +
@@ -108,7 +108,7 @@

Team: {{ team.team_name }}

Edit Team

-
+ {% csrf_token %}
@@ -127,34 +127,16 @@

Edit Team

-
- Payment Status: {{ team.get_payment_status_display }} -
- {% if team.payment_status == "P" or team.payment_status == "E" %} - {% endif %} - - {% 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

@@ -191,7 +173,7 @@

Active Invites

{{ forloop.counter }} {{ invite.invite_email }} - Revoke @@ -202,7 +184,7 @@

Active Invites

You have no active invites.

{% endif %}

Send Invite

-
+ {% csrf_token %}
{% 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 Impulse. - -
+
+ + + + + YOU ARE NOT IN A TEAM. Please create a team or join an existing team to complete your registration for Impulse. Even if you plan on participating individually, you will have to create a team. + +

Current Invites

{% if invites_for_user %} @@ -255,9 +235,9 @@

Current Invites

{{ invite.inviting_team.team_name }} {{ invite.inviting_team.team_leader }} - Accept - Reject {% endfor %} @@ -272,7 +252,7 @@

Current Invites

Create a team

-
+ {% csrf_token %}
@@ -291,13 +271,7 @@

Create a team

-
- 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 %}
@@ -354,4 +328,17 @@

Announcements

ieeeCheckbox.addEventListener("change", toggleIEEE); }); + + {% endblock %} diff --git a/corpus/templates/emails/electrika/announcement.html b/corpus/templates/emails/electrika/announcement.html new file mode 100644 index 00000000..6fc2e292 --- /dev/null +++ b/corpus/templates/emails/electrika/announcement.html @@ -0,0 +1,17 @@ +{% extends 'emails/base.html' %} + +{% block title %} + Announcement | Electrika +{% endblock %} + +{% block content %} +

Announcement - Electrika

+
+ {{ announcement.content | linebreaks }} +
+ {% if announcement.url_link %} +

+ {{ announcement.url_link_text }} +

+ {% endif %} +{% endblock %} From 8c24c673870e1e00044479d89372190bde293161 Mon Sep 17 00:00:00 2001 From: Vignaraj-pai Date: Mon, 15 Jan 2024 14:14:58 +0530 Subject: [PATCH 3/3] fix particle-js issue, added team,user info download options, added team me up option. --- corpus/electrika/migrations/0001_initial.py | 3 +- corpus/electrika/models.py | 1 + corpus/electrika/urls.py | 12 +- corpus/electrika/views.py | 120 +++++++++++++++++- corpus/package.json | 2 + corpus/templates/base.html | 4 +- corpus/templates/electrika/admin/admin.html | 17 +-- .../templates/electrika/admin/team_page.html | 33 ++++- corpus/templates/electrika/admin/teams.html | 76 +++++++---- corpus/templates/electrika/admin/users.html | 35 ++++- corpus/templates/electrika/base.html | 77 ++++++++++- corpus/templates/electrika/home.html | 93 ++++++++------ corpus/templates/electrika/index.html | 95 +++++++++++--- corpus/templates/electrika/register.html | 2 +- corpus/templates/impulse/index.html | 2 +- .../templates/static/img/electrika/Naman.jpg | Bin 0 -> 133434 bytes .../{particles.json => particles-dark.json} | 0 17 files changed, 462 insertions(+), 110 deletions(-) create mode 100644 corpus/templates/static/img/electrika/Naman.jpg rename corpus/templates/static/js/electrika/{particles.json => particles-dark.json} (100%) diff --git a/corpus/electrika/migrations/0001_initial.py b/corpus/electrika/migrations/0001_initial.py index cb5251ad..a9e8ca76 100644 --- a/corpus/electrika/migrations/0001_initial.py +++ b/corpus/electrika/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.7 on 2024-01-12 14:03 +# Generated by Django 4.2.7 on 2024-01-15 08:43 from django.conf import settings from django.db import migrations, models @@ -35,6 +35,7 @@ class Migration(migrations.Migration): ('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)), + ('to_be_teamed_up', models.BooleanField(default=False)), ], ), migrations.CreateModel( diff --git a/corpus/electrika/models.py b/corpus/electrika/models.py index d69882d5..55f95a03 100644 --- a/corpus/electrika/models.py +++ b/corpus/electrika/models.py @@ -14,6 +14,7 @@ class ElectrikaUser(models.Model): team = models.ForeignKey( "Team", on_delete=models.CASCADE, related_name="team", blank=True, null=True ) + to_be_teamed_up = models.BooleanField(default=False) def __str__(self): return self.user.email diff --git a/corpus/electrika/urls.py b/corpus/electrika/urls.py index 25420341..fb42fcd8 100644 --- a/corpus/electrika/urls.py +++ b/corpus/electrika/urls.py @@ -13,8 +13,11 @@ path( "delete_invite/", views.delete_invite, name="electrika_delete_invite" ), + path("teamify/optin", views.opt_in, name="electrika_opt_in"), + path("teamify/optout", views.opt_out, name="electrika_opt_out"), path("admin", views.admin, name="electrika_admin"), path("admin/teams", views.team_management, name="electrika_admin_teams"), + path("admin/teams/create", views.create_team_admin, name="electrika_admin_team_create"), path("admin/team/", views.team_page, name="electrika_admin_team_page"), path("admin/users", views.user_management, name="electrika_admin_users"), path( @@ -28,9 +31,8 @@ name="electrika_delete_announcement", ), path( - "admin/download_csv", - views.download_csv_non_registrants, - name="electrika_download_csv", - ), - + "admin/team/download_csv", + views.team_download, + name="electrika_admin_download_teams_csv", + ) ] \ No newline at end of file diff --git a/corpus/electrika/views.py b/corpus/electrika/views.py index 322f40d6..c22aa6c0 100644 --- a/corpus/electrika/views.py +++ b/corpus/electrika/views.py @@ -17,7 +17,7 @@ from corpus.decorators import ensure_group_membership from corpus.decorators import module_enabled from corpus.utils import send_email - +from django.db import transaction # Create your views here. @module_enabled(module_name="electrika") def home(request): @@ -40,8 +40,6 @@ def home(request): config["reg_end_datetime"], ) - print(reg_start_datetime, reg_end_datetime) - reg_start_datetime, reg_end_datetime = datetime.strptime( reg_start_datetime, DATETIME_FORMAT ), datetime.strptime(reg_end_datetime, DATETIME_FORMAT) @@ -55,8 +53,6 @@ def home(request): args["registration_active"] = registration_active args["registration_done"] = registration_done - print(args) - return render( request, "electrika/home.html", @@ -87,6 +83,8 @@ def index(request): max_count = int(config["max_team_size"]) + args["max_count"] = max_count + if team_count >= max_count: args["team_full"] = True if team.team_leader == electrika_user: @@ -307,6 +305,24 @@ def delete_invite(request, pk): messages.success(request, "Invite deleted!") return redirect("electrika_index") +@login_required +@module_enabled(module_name="electrika") +def opt_in(request): + electrika_user = ElectrikaUser.objects.get(user=request.user) + electrika_user.to_be_teamed_up = True + electrika_user.save() + messages.success(request, "Successfully opted in for team formation!") + return redirect("electrika_index") + +@login_required +@module_enabled(module_name="electrika") +def opt_out(request): + electrika_user = ElectrikaUser.objects.get(user=request.user) + electrika_user.to_be_teamed_up = False + electrika_user.save() + messages.success(request, "Successfully opted out for team formation!") + return redirect("electrika_index") + @login_required @ensure_group_membership(group_names=["electrika_admin"]) @@ -329,6 +345,74 @@ def team_page(request, pk): args["members"] = ElectrikaUser.objects.filter(team=team) return render(request, "electrika/admin/team_page.html", args) +@login_required +@ensure_group_membership(group_names=["electrika_admin"]) +def create_team_admin(request): + import random + electrika_users = ElectrikaUser.objects.filter(team=None, to_be_teamed_up=True) + TEAM_NAMES = [ + "Iconoclasts", + "Nihilists", + "Antagonists", + "Whiz Kids", + "The Geek Squad", + "Net Surfers", + "The Informants", + "Black Hat Hackers", + "Brainiacs", + "Quizzical Education", + "Phone a Friend", + "Witches and Quizards", + "The Quizzy Bees", + "Wallflowers", + "Smart Simpson", + "Cheat Sheet", + "You Cheated Off Us in High School", + "Brainstormers" + ] + + num_users = electrika_users.count() + num_teams = num_users // 4 + + if num_users == 0: + messages.error(request, "Not enough users to create teams!") + return redirect("electrika_admin_teams") + + try: + with transaction.atomic(): + for i in range(num_teams): + team_name = TEAM_NAMES[random.randint(0, len(TEAM_NAMES) - 1)] + team = Team(team_name=team_name, team_leader=electrika_users[i*4]) + team.save() + for j in range(4): + index = i*4 + j + if index >= electrika_users.count(): + break + user = electrika_users[index] + user.team = team + user.to_be_teamed_up = False + user.save() + + if num_users % 4 != 0: + team_name = TEAM_NAMES[num_teams%len(TEAM_NAMES)] + team = Team(team_name=team_name, team_leader=electrika_users[num_teams*4]) + team.save() + for j in range(num_users % 4): + index = num_teams*4 + j + if index >= electrika_users.count(): + break + user = electrika_users[num_teams*4 + j] + user.team = team + user.to_be_teamed_up = False + user.save() + except Exception as e: + messages.error(request, "Error creating teams!") + return redirect("electrika_admin_teams") + + messages.success(request, "Successfully created teams!") + return redirect("electrika_admin_teams") + + @login_required @ensure_group_membership(group_names=["electrika_admin"]) def user_management(request): @@ -425,3 +509,29 @@ def download_csv_non_registrants(request): writer.writerow([user, user.email]) return response + +@login_required +@ensure_group_membership(group_names=["electrika_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) + + queryset = Team.objects.select_related('team_leader__user').values('team_name', 'team_leader__user__first_name', 'team_leader__user__email', 'team_leader__user__phone_no') + selected_fields = request.POST.getlist('selected_fields[]') + + # Write header row + header_row = selected_fields + writer.writerow(header_row) + + # Write data rows + for item in queryset: + row_data = [str(item[field]) for field in selected_fields] + + writer.writerow(row_data) + + return response \ No newline at end of file diff --git a/corpus/package.json b/corpus/package.json index 9dbece05..846432f4 100644 --- a/corpus/package.json +++ b/corpus/package.json @@ -20,3 +20,5 @@ }, "version": "1.0.0" } + + diff --git a/corpus/templates/base.html b/corpus/templates/base.html index 0f10afb7..41674877 100644 --- a/corpus/templates/base.html +++ b/corpus/templates/base.html @@ -78,7 +78,9 @@ {% block content %} {% endblock %} -{% include "components/dark_mode_toggle.html" %} +{% block dark_mode_toggle %} + {% include "components/dark_mode_toggle.html" %} +{% endblock %} {% include "components/general_footer.html" %}