From d6c8b91187259c345e705bf58398485c5e545fff Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Thu, 20 Aug 2020 14:26:40 -0500 Subject: [PATCH 01/57] Cleanup. Needs review --- .isort.cfg | 10 + .vscode/settings.json | 6 +- accounts/admin.py | 2 - accounts/apps.py | 2 +- accounts/models.py | 2 - accounts/tests.py | 2 - accounts/urls.py | 8 +- accounts/views.py | 207 ++-- coderdojochi/__init__.py | 2 +- coderdojochi/admin.py | 835 ++++++------- coderdojochi/apps.py | 2 +- coderdojochi/context_processors.py | 14 +- coderdojochi/cron.py | 221 ++-- coderdojochi/factories.py | 8 +- coderdojochi/forms.py | 281 +---- coderdojochi/middleware.py | 7 +- .../migrations/0036_auto_20200812_1246.py | 61 + coderdojochi/mixins.py | 19 +- coderdojochi/models.py | 1084 ++++------------- coderdojochi/notifications.py | 106 +- coderdojochi/old_views.py | 796 ++++-------- coderdojochi/settings.py | 303 +++-- coderdojochi/signals_handlers.py | 23 +- coderdojochi/social_account_adapter.py | 8 +- .../templatetags/coderdojochi_extras.py | 81 +- coderdojochi/tests/test_mentor_updates.py | 4 +- coderdojochi/tests/test_password_session.py | 125 +- coderdojochi/urls.py | 298 +++-- coderdojochi/util.py | 32 +- coderdojochi/views/calendar.py | 41 +- coderdojochi/views/meetings.py | 226 ++-- coderdojochi/views/profile.py | 123 +- coderdojochi/views/sessions.py | 380 +++--- coderdojochi/views/welcome.py | 130 +- pyproject.toml | 32 + tasks.py | 31 +- weallcode/__init__.py | 2 +- weallcode/admin.py | 157 +-- weallcode/apps.py | 5 +- weallcode/forms.py | 72 +- weallcode/models.py | 109 +- weallcode/templatetags/active.py | 6 +- weallcode/tests.py | 2 - weallcode/urls.py | 49 +- weallcode/views.py | 165 +-- 45 files changed, 2238 insertions(+), 3841 deletions(-) create mode 100644 .isort.cfg create mode 100644 coderdojochi/migrations/0036_auto_20200812_1246.py create mode 100644 pyproject.toml diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..259a20ba --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,10 @@ +[settings] +line_length = 120 +multi_line_output = 3 +include_trailing_comma = true +known_django = django +sections = FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER +skip = venv/ +skip_glob = **/migrations/*.py +force_grid_wrap = 0 +use_parentheses = true diff --git a/.vscode/settings.json b/.vscode/settings.json index df3974f0..bbc6235e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,7 @@ "**/site-packages/**/*.py", "**/migrations/**" ], + "python.sortImports.args": ["--settings-path=${workspaceFolder}/setup.cfg"], "python.linting.enabled": true, "python.linting.pycodestyleEnabled": false, "python.linting.pycodestylePath": "pycodestyle", @@ -35,9 +36,12 @@ "[python]": { "editor.rulers": [120], "editor.codeActionsOnSave": { - "source.organizeImports": true + // it was `"source.organizeImports": true` in my first version of this post, + // see below comment for explanation. + "source.organizeImports.python": true } }, + "[git-commit]": { "editor.rulers": [72] }, diff --git a/accounts/admin.py b/accounts/admin.py index 8c38f3f3..846f6b40 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -1,3 +1 @@ -from django.contrib import admin - # Register your models here. diff --git a/accounts/apps.py b/accounts/apps.py index 9b3fc5a4..fb0257e0 100644 --- a/accounts/apps.py +++ b/accounts/apps.py @@ -2,4 +2,4 @@ class AccountsConfig(AppConfig): - name = 'accounts' + name = "accounts" diff --git a/accounts/models.py b/accounts/models.py index 71a83623..6b202199 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -1,3 +1 @@ -from django.db import models - # Create your models here. diff --git a/accounts/tests.py b/accounts/tests.py index 7ce503c2..a39b155a 100644 --- a/accounts/tests.py +++ b/accounts/tests.py @@ -1,3 +1 @@ -from django.test import TestCase - # Create your tests here. diff --git a/accounts/urls.py b/accounts/urls.py index 52226cb7..4cc97268 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -4,8 +4,8 @@ from .views import AccountHomeView, LoginView, SignupView urlpatterns = [ - path('', AccountHomeView.as_view(), name='account_home'), - path('login/', LoginView.as_view(), name='account_login'), - path('signup/', SignupView.as_view(), name='account_signup'), - path('', include('allauth.urls')), + path("", AccountHomeView.as_view(), name="account_home"), + path("login/", LoginView.as_view(), name="account_login"), + path("signup/", SignupView.as_view(), name="account_signup"), + path("", include("allauth.urls")), ] diff --git a/accounts/views.py b/accounts/views.py index ec801b10..fd47d7aa 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -15,7 +15,7 @@ class SignupView(MetadataMixin, AllAuthSignupView): - template_name = 'account/signup.html' + template_name = "account/signup.html" title = f"Sign up | {settings.SITE_NAME}" description = ( "We All Code is volunteer run nonprofit organization that teaches web, game, and app development to " @@ -28,7 +28,7 @@ class SignupView(MetadataMixin, AllAuthSignupView): class LoginView(MetadataMixin, AllAuthLoginView): - template_name = 'account/login.html' + template_name = "account/login.html" title = f"Login | {settings.SITE_NAME}" description = ( "We All Code is volunteer run nonprofit organization that teaches web, game, and app development to " @@ -40,36 +40,29 @@ class LoginView(MetadataMixin, AllAuthLoginView): twitter_creator = "@weallcode" -@method_decorator(login_required, name='dispatch') +@method_decorator(login_required, name="dispatch") class AccountHomeView(TemplateView): def dispatch(self, *args, **kwargs): if not self.request.user.role: - if 'next' in self.request.GET: - return redirect( - f"{reverse('welcome')}?next={self.request.GET['next']}" - ) + if "next" in self.request.GET: + return redirect(f"{reverse('welcome')}?next={self.request.GET['next']}") else: - messages.warning( - self.request, - 'Tell us a little about yourself before going on account.' - ) - return redirect('welcome') + messages.warning(self.request, "Tell us a little about yourself before going on account.") + return redirect("welcome") return super().dispatch(*args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['highlight'] = ( - self.request.GET['highlight'] if 'highlight' in self.request.GET else False - ) + context["highlight"] = self.request.GET["highlight"] if "highlight" in self.request.GET else False - context['user'] = self.request.user + context["user"] = self.request.user - if self.request.user.role == 'mentor': + if self.request.user.role == "mentor": return {**context, **self.get_context_data_for_mentor()} - if self.request.user.role == 'guardian': + if self.request.user.role == "guardian": return {**context, **self.get_context_data_for_guardian()} return context @@ -77,188 +70,140 @@ def get_context_data(self, **kwargs): def get_context_data_for_mentor(self): mentor = get_object_or_404(Mentor, user=self.request.user) - orders = MentorOrder.objects.select_related().filter( - is_active=True, - mentor=mentor, - ) - - upcoming_sessions = orders.filter( - is_active=True, - session__start_date__gte=timezone.now() - ).order_by('session__start_date') + orders = MentorOrder.objects.select_related().filter(is_active=True, mentor=mentor,) - past_sessions = orders.filter( - is_active=True, - session__start_date__lte=timezone.now() - ).order_by('session__start_date') + upcoming_sessions = orders.filter(is_active=True, session__start_date__gte=timezone.now()).order_by( + "session__start_date" + ) - meeting_orders = MeetingOrder.objects.select_related().filter( - mentor=mentor + past_sessions = orders.filter(is_active=True, session__start_date__lte=timezone.now()).order_by( + "session__start_date" ) + meeting_orders = MeetingOrder.objects.select_related().filter(mentor=mentor) + upcoming_meetings = meeting_orders.filter( - is_active=True, - meeting__is_public=True, - meeting__end_date__gte=timezone.now() - ).order_by('meeting__start_date') + is_active=True, meeting__is_public=True, meeting__end_date__gte=timezone.now() + ).order_by("meeting__start_date") account_complete = False if ( - mentor.user.first_name and - mentor.user.last_name and - mentor.avatar and - mentor.background_check and - past_sessions.count() > 0 + mentor.user.first_name + and mentor.user.last_name + and mentor.avatar + and mentor.background_check + and past_sessions.count() > 0 ): account_complete = True return { - 'mentor': mentor, - 'orders': orders, - 'upcoming_sessions': upcoming_sessions, - 'past_sessions': upcoming_sessions, - 'meeting_orders': meeting_orders, - 'upcoming_meetings': upcoming_meetings, - 'account_complete': account_complete + "mentor": mentor, + "orders": orders, + "upcoming_sessions": upcoming_sessions, + "past_sessions": upcoming_sessions, + "meeting_orders": meeting_orders, + "upcoming_meetings": upcoming_meetings, + "account_complete": account_complete, } def get_context_data_for_guardian(self): - guardian = get_object_or_404( - Guardian, - user=self.request.user - ) + guardian = get_object_or_404(Guardian, user=self.request.user) - students = Student.objects.filter( - is_active=True, - guardian=guardian, - ) + students = Student.objects.filter(is_active=True, guardian=guardian,) - student_orders = Order.objects.filter( - student__in=students, - ) + student_orders = Order.objects.filter(student__in=students,) - upcoming_orders = student_orders.filter( - is_active=True, - session__start_date__gte=timezone.now(), - ).order_by('session__start_date') + upcoming_orders = student_orders.filter(is_active=True, session__start_date__gte=timezone.now(),).order_by( + "session__start_date" + ) - past_orders = student_orders.filter( - is_active=True, - session__start_date__lte=timezone.now(), - ).order_by('session__start_date') + past_orders = student_orders.filter(is_active=True, session__start_date__lte=timezone.now(),).order_by( + "session__start_date" + ) return { - 'guardian': guardian, - 'students': students, - 'student_orders': student_orders, - 'upcoming_orders': upcoming_orders, - 'past_orders': past_orders, + "guardian": guardian, + "students": students, + "student_orders": student_orders, + "upcoming_orders": upcoming_orders, + "past_orders": past_orders, } def get(self, *args, **kwargs): - if self.request.user.role == 'mentor': + if self.request.user.role == "mentor": return self.get_for_mentor(**kwargs) - if self.request.user.role == 'guardian': + if self.request.user.role == "guardian": return self.get_for_guardian(**kwargs) def get_for_mentor(self, **kwargs): context = self.get_context_data(**kwargs) - context['form'] = MentorForm(instance=context['mentor']) - context['user_form'] = CDCModelForm(instance=context['mentor'].user) + context["form"] = MentorForm(instance=context["mentor"]) + context["user_form"] = CDCModelForm(instance=context["mentor"].user) - return render(self.request, 'account/mentor/home.html', context) + return render(self.request, "account/mentor/home.html", context) def get_for_guardian(self, **kwargs): context = self.get_context_data(**kwargs) - context['form'] = GuardianForm(instance=context['guardian']) - context['user_form'] = CDCModelForm(instance=context['guardian'].user) + context["form"] = GuardianForm(instance=context["guardian"]) + context["user_form"] = CDCModelForm(instance=context["guardian"].user) - return render(self.request, 'account/guardian/home.html', context) + return render(self.request, "account/guardian/home.html", context) def post(self, *args, **kwargs): - if self.request.user.role == 'mentor': + if self.request.user.role == "mentor": return self.post_for_mentor(**kwargs) - if self.request.user.role == 'guardian': + if self.request.user.role == "guardian": return self.post_for_guardian(**kwargs) def post_for_mentor(self, **kwargs): context = self.get_context_data(**kwargs) - mentor = context['mentor'] + mentor = context["mentor"] - form = MentorForm( - self.request.POST, - self.request.FILES, - instance=mentor - ) + form = MentorForm(self.request.POST, self.request.FILES, instance=mentor) - user_form = CDCModelForm( - self.request.POST, - self.request.FILES, - instance=mentor.user - ) + user_form = CDCModelForm(self.request.POST, self.request.FILES, instance=mentor.user) - if ( - form.is_valid() and - user_form.is_valid() - ): + if form.is_valid() and user_form.is_valid(): form.save() user_form.save() - messages.success( - self.request, - 'Profile information saved.' - ) + messages.success(self.request, "Profile information saved.") - return redirect('account_home') + return redirect("account_home") else: - messages.error( - self.request, - 'There was an error. Please try again.' - ) + messages.error(self.request, "There was an error. Please try again.") - context['form'] = form - context['user_form'] = user_form + context["form"] = form + context["user_form"] = user_form - return render(self.request, 'account/mentor/home.html', context) + return render(self.request, "account/mentor/home.html", context) def post_for_guardian(self, **kwargs): context = self.get_context_data(**kwargs) - guardian = context['guardian'] + guardian = context["guardian"] - form = GuardianForm( - self.request.POST, - instance=guardian - ) + form = GuardianForm(self.request.POST, instance=guardian) - user_form = CDCModelForm( - self.request.POST, - instance=guardian.user - ) + user_form = CDCModelForm(self.request.POST, instance=guardian.user) if form.is_valid() and user_form.is_valid(): form.save() user_form.save() - messages.success( - self.request, - 'Profile information saved.' - ) + messages.success(self.request, "Profile information saved.") - return redirect('account_home') + return redirect("account_home") else: - messages.error( - self.request, - 'There was an error. Please try again.' - ) + messages.error(self.request, "There was an error. Please try again.") - context['form'] = form - context['user_form'] = user_form + context["form"] = form + context["user_form"] = user_form - return render(self.request, 'account/guardian/home.html', context) + return render(self.request, "account/guardian/home.html", context) diff --git a/coderdojochi/__init__.py b/coderdojochi/__init__.py index 42934929..bd844805 100644 --- a/coderdojochi/__init__.py +++ b/coderdojochi/__init__.py @@ -1 +1 @@ -default_app_config = 'coderdojochi.apps.CoderDojoChiConfig' +default_app_config = "coderdojochi.apps.CoderDojoChiConfig" diff --git a/coderdojochi/admin.py b/coderdojochi/admin.py index d191a4d9..c39f7b93 100644 --- a/coderdojochi/admin.py +++ b/coderdojochi/admin.py @@ -36,68 +36,69 @@ @admin.register(User) class UserAdmin(ImportExportActionModelAdmin): list_display = [ - 'email', - 'first_name', - 'last_name', - 'role_link', - 'date_joined', - 'last_login', - 'is_active', - 'is_staff', - 'is_superuser', + "email", + "first_name", + "last_name", + "role_link", + "date_joined", + "last_login", + "is_active", + "is_staff", + "is_superuser", ] list_filter = [ - 'role', - 'is_active', - 'is_staff', - 'date_joined', + "role", + "is_active", + "is_staff", + "date_joined", ] ordering = [ - '-date_joined', + "-date_joined", ] - date_hierarchy = 'date_joined' + date_hierarchy = "date_joined" search_fields = [ - 'first_name', - 'last_name', - 'email', + "first_name", + "last_name", + "email", ] filter_horizontal = [ - 'groups', - 'user_permissions', + "groups", + "user_permissions", ] readonly_fields = [ - 'password', - 'last_login', + "password", + "last_login", ] view_on_site = False - change_form_template = 'loginas/change_form.html' + change_form_template = "loginas/change_form.html" def role_link(self, obj): - if obj.role == 'mentor': + if obj.role == "mentor": return format_html( '{role}', - url=reverse('admin:coderdojochi_mentor_changelist'), + url=reverse("admin:coderdojochi_mentor_changelist"), query=obj.email, role=obj.role, ) - elif obj.role == 'guardian': + elif obj.role == "guardian": return format_html( '{role}', - url=reverse('admin:coderdojochi_guardian_changelist'), + url=reverse("admin:coderdojochi_guardian_changelist"), query=obj.email, role=obj.role, ) return obj.role - role_link.short_description = 'Role' + + role_link.short_description = "Role" @admin.register(Mentor) @@ -105,48 +106,48 @@ class MentorAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 50 list_display = [ - 'first_name', - 'last_name', - 'user_link', - 'mentor_count_link', - 'created_at', - 'updated_at', - 'is_active', - 'is_public', - 'background_check', - 'avatar_approved', + "first_name", + "last_name", + "user_link", + "mentor_count_link", + "created_at", + "updated_at", + "is_active", + "is_public", + "background_check", + "avatar_approved", ] list_filter = [ - 'is_active', - 'is_public', - 'background_check', - 'avatar_approved', + "is_active", + "is_public", + "background_check", + "avatar_approved", ] list_select_related = [ - 'user', + "user", ] ordering = [ - '-created_at', + "-created_at", ] - date_hierarchy = 'created_at' + date_hierarchy = "created_at" search_fields = [ - 'user__first_name', - 'user__last_name', - 'user__username', - 'user__email', + "user__first_name", + "user__last_name", + "user__username", + "user__email", ] readonly_fields = [ - 'mentor_count_link', + "mentor_count_link", ] autocomplete_fields = [ - 'user', + "user", ] def view_on_site(self, obj): @@ -156,76 +157,64 @@ def get_queryset(self, request): qs = super(MentorAdmin, self).get_queryset(request) # Count all orders that are marked as active - qs = qs.annotate( - mentororder__count=Count( - Case( - When( - mentororder__is_active=True, - then=1, - ) - ) - ) - ) + qs = qs.annotate(mentororder__count=Count(Case(When(mentororder__is_active=True, then=1,)))) return qs def mentor_count_link(self, obj): return format_html( '{count}', - url=reverse('admin:coderdojochi_mentororder_changelist'), + url=reverse("admin:coderdojochi_mentororder_changelist"), query=obj.id, count=obj.mentororder__count, ) - mentor_count_link.short_description = 'Orders' - mentor_count_link.admin_order_field = 'mentororder__count' + + mentor_count_link.short_description = "Orders" + mentor_count_link.admin_order_field = "mentororder__count" def user_link(self, obj): return format_html( '{user}', - url=reverse('admin:coderdojochi_cdcuser_change', args=(obj.user.id,)), + url=reverse("admin:coderdojochi_cdcuser_change", args=(obj.user.id,)), user=obj.user, ) - user_link.short_description = 'User' + + user_link.short_description = "User" class GuardianImportResource(resources.ModelResource): - first_name = Field(attribute='first_name', column_name='first_name') - last_name = Field(attribute='last_name', column_name='last_name') - email = Field(attribute='email', column_name='email') - phone = Field(attribute='phone', column_name='phone') - zip = Field(attribute='zip', column_name='zip') + first_name = Field(attribute="first_name", column_name="first_name") + last_name = Field(attribute="last_name", column_name="last_name") + email = Field(attribute="email", column_name="email") + phone = Field(attribute="phone", column_name="phone") + zip = Field(attribute="zip", column_name="zip") def get_or_init_instance(self, instance_loader, row): """ Either fetches an already existing instance or initializes a new one. """ try: - instance = User.objects.get(email=row['email']) + instance = User.objects.get(email=row["email"]) except User.DoesNotExist: return Guardian(user=User()), True else: return (Guardian.objects.get(user=instance), False) def import_obj(self, obj, data, dry_run): - first_name = data.get('first_name') - last_name = data.get('last_name') - email = data.get('email') - zip = data.get('zip') - phone = data.get('phone') if obj.pk: user = obj.user else: user = User( - first_name=first_name, - last_name=last_name, - email=email, - role='guardian', - username=email, + first_name=data.get("first_name"), + last_name=data.get("last_name"), + email=data.get("email"), + role="guardian", + username=data.get("email"), ) obj.user = user obj.is_active = False - obj.zip = zip - obj.phone = phone + obj.zip = data.get("zip") + obj.phone = data.get("phone") if not dry_run: user.save() obj.user = user @@ -234,11 +223,11 @@ def import_obj(self, obj, data, dry_run): class Meta: model = Guardian import_id_fields = [ - 'phone', + "phone", ] fields = [ - 'phone', - 'zip', + "phone", + "zip", ] @@ -247,39 +236,39 @@ class GuardianAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 10 list_display = [ - 'first_name', - 'last_name', - 'student_count_link', - 'user_link', - 'created_at', - 'updated_at', + "first_name", + "last_name", + "student_count_link", + "user_link", + "created_at", + "updated_at", ] list_filter = [ - 'zip', + "zip", ] list_select_related = [ - 'user', + "user", ] search_fields = [ - 'user__first_name', - 'user__last_name', - 'user__username', - 'user__email', - 'phone', + "user__first_name", + "user__last_name", + "user__username", + "user__email", + "phone", ] readonly_fields = [ - 'student_count_link', + "student_count_link", ] autocomplete_fields = [ - 'user', + "user", ] - date_hierarchy = 'created_at' + date_hierarchy = "created_at" view_on_site = False @@ -289,90 +278,58 @@ class GuardianAdmin(ImportExportMixin, ImportExportActionModelAdmin): def get_queryset(self, request): qs = super(GuardianAdmin, self).get_queryset(request) qs = qs.select_related() - qs = qs.annotate( - student_count=Count('student') - ).order_by('-student_count') + qs = qs.annotate(student_count=Count("student")).order_by("-student_count") return qs def user_link(self, obj): return format_html( '{name}', - url=reverse('admin:coderdojochi_cdcuser_change', args=(obj.user.id,)), + url=reverse("admin:coderdojochi_cdcuser_change", args=(obj.user.id,)), name=obj.user, ) - user_link.short_description = 'User' + + user_link.short_description = "User" def student_count_link(self, obj): return format_html( '{count}', - url=reverse('admin:coderdojochi_student_changelist'), + url=reverse("admin:coderdojochi_student_changelist"), query=obj.id, count=obj.student_count, ) - student_count_link.short_description = 'Students' - student_count_link.admin_order_field = 'student_count' + + student_count_link.short_description = "Students" + student_count_link.admin_order_field = "student_count" class StudentResource(resources.ModelResource): - first_name = Field( - attribute='first_name', - column_name='first_name', - ) - last_name = Field( - attribute='last_name', - column_name='last_name', - ) - guardian_email = Field( - attribute='guardian_email', - column_name='guardian_email', - ) - birthday = Field( - attribute='birthday', - column_name='birthday', - ) - gender = Field( - attribute='gender', - column_name='gender', - ) - school_name = Field( - attribute='school_name', - column_name='school_name', - ) - school_type = Field( - attribute='school_type', - column_name='school_type', - ) - photo_release = Field( - attribute='photo_release', - column_name='photo_release', - ) - consent = Field( - attribute='consent', - column_name='consent', - ) + first_name = Field(attribute="first_name", column_name="first_name",) + last_name = Field(attribute="last_name", column_name="last_name",) + guardian_email = Field(attribute="guardian_email", column_name="guardian_email",) + birthday = Field(attribute="birthday", column_name="birthday",) + gender = Field(attribute="gender", column_name="gender",) + school_name = Field(attribute="school_name", column_name="school_name",) + school_type = Field(attribute="school_type", column_name="school_type",) + photo_release = Field(attribute="photo_release", column_name="photo_release",) + consent = Field(attribute="consent", column_name="consent",) def import_obj(self, obj, data, dry_run): - guardian_email = data.get('guardian_email') - - obj.first_name = data.get('first_name') - obj.last_name = data.get('last_name') - obj.birthday = datetime.strptime( - data.get('birthday', ''), - '%m/%d/%Y' - ) - obj.gender = data.get('gender', '') - obj.school_name = data.get('school_name', '') - obj.school_type = data.get('school_type', '') - obj.photo_release = strtobool(data.get('photo_release', '')) - obj.consent = strtobool(data.get('consent', '')) + guardian_email = data.get("guardian_email") + + obj.first_name = data.get("first_name") + obj.last_name = data.get("last_name") + obj.birthday = datetime.strptime(data.get("birthday", ""), "%m/%d/%Y") + obj.gender = data.get("gender", "") + obj.school_name = data.get("school_name", "") + obj.school_type = data.get("school_type", "") + obj.photo_release = strtobool(data.get("photo_release", "")) + obj.consent = strtobool(data.get("consent", "")) obj.is_active = True try: obj.guardian = Guardian.objects.get(user__email=guardian_email) except Guardian.DoesNotExist: - raise ImportError( - f'guardian with email {guardian_email} not found' - ) + raise ImportError(f"guardian with email {guardian_email} not found") if not dry_run: obj.save() @@ -380,12 +337,12 @@ def import_obj(self, obj, data, dry_run): class Meta: model = Student import_id_fields = [ - 'first_name', - 'last_name', + "first_name", + "last_name", ] fields = [ - 'first_name', - 'last_name', + "first_name", + "last_name", ] @@ -394,40 +351,40 @@ class StudentAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 10 list_display = [ - 'first_name', - 'last_name', - 'gender', - 'get_clean_gender', - 'guardian_link', - 'order_count_link', - 'get_age', - 'is_active', + "first_name", + "last_name", + "gender", + "get_clean_gender", + "guardian_link", + "order_count_link", + "get_age", + "is_active", ] list_filter = [ - 'gender', + "gender", ] filter_horizontal = [ - 'race_ethnicity', + "race_ethnicity", ] ordering = [ - 'guardian', + "guardian", ] search_fields = [ - 'first_name', - 'last_name', - 'guardian__user__first_name', - 'guardian__user__last_name', + "first_name", + "last_name", + "guardian__user__first_name", + "guardian__user__last_name", ] autocomplete_fields = [ - 'guardian', + "guardian", ] - date_hierarchy = 'created_at' + date_hierarchy = "created_at" view_on_site = False @@ -435,8 +392,8 @@ class StudentAdmin(ImportExportMixin, ImportExportActionModelAdmin): resource_class = StudentResource readonly_fields = [ - 'guardian_link', - 'order_count_link', + "guardian_link", + "order_count_link", ] def get_queryset(self, request): @@ -445,16 +402,7 @@ def get_queryset(self, request): qs = qs.select_related() # Count all orders that are marked as active - qs = qs.annotate( - order__count=Count( - Case( - When( - order__is_active=True, - then=1, - ) - ) - ) - ) + qs = qs.annotate(order__count=Count(Case(When(order__is_active=True, then=1,)))) return qs def guardian_link(self, obj): @@ -464,17 +412,19 @@ def guardian_link(self, obj): first=obj.guardian.user.first_name, last=obj.guardian.user.last_name, ) - guardian_link.short_description = 'Guardian' + + guardian_link.short_description = "Guardian" def order_count_link(self, obj): return format_html( '{count}', - url=reverse('admin:coderdojochi_order_changelist'), + url=reverse("admin:coderdojochi_order_changelist"), student=obj.id, count=obj.order__count, ) - order_count_link.short_description = 'Orders' - order_count_link.admin_order_field = 'order__count' + + order_count_link.short_description = "Orders" + order_count_link.admin_order_field = "order__count" @admin.register(Course) @@ -482,29 +432,33 @@ class CourseAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 50 list_display = [ - 'code', - 'title', - 'course_type', - 'duration', + "code", + "title", + "course_type", + "duration", ] list_filter = [ - 'is_active', - 'code', - 'course_type', - 'duration', + "is_active", + "code", + "course_type", + "duration", ] ordering = [ - 'code', - 'created_at', + "code", + "created_at", ] search_fields = [ - 'title', - 'description', + "title", + "description", ] + prepopulated_fields = { + "slug": ("title",), + } + view_on_site = False @@ -513,39 +467,39 @@ class SessionAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 10 list_display = [ - '_course', - 'start_date', + "_course", + "start_date", # 'end_date', - 'location', - 'capacity', - 'student_count_link', - 'mentor_count_link', - 'is_active', - 'is_public', - 'is_guardian_announced', + "location", + "capacity", + "student_count_link", + "mentor_count_link", + "is_active", + "is_public", + "is_guardian_announced", ] list_filter = [ - 'is_active', - 'is_public', - 'course__code', - 'course__title', - 'location', + "is_active", + "is_public", + "course__code", + "course__title", + "location", ] list_select_related = [ - 'course', - 'location', - 'instructor', + "course", + "location", + "instructor", ] ordering = [ - '-start_date', + "-start_date", ] filter_horizontal = [ - 'waitlist_mentors', - 'waitlist_students', + "waitlist_mentors", + "waitlist_students", ] autocomplete_fields = [ @@ -554,59 +508,66 @@ class SessionAdmin(ImportExportMixin, ImportExportActionModelAdmin): # 'instructor', ] - search_fields = [ - 'start_date' - ] + search_fields = ["start_date"] - date_hierarchy = 'start_date' + date_hierarchy = "start_date" fieldsets = ( - (None, { - # 'classes': ('collapse',), - 'fields': ( - 'course', - 'location', - 'start_date', - # 'old_end_date', - # 'old_mentor_start_date', - # 'old_mentor_end_date', - 'capacity', - 'mentor_capacity', - 'instructor', - 'is_active', - 'is_public', - ), - }), - ('Overrides', { - # 'classes': ('collapse',), - 'fields': ( - 'override_minimum_age_limitation', - 'override_maximum_age_limitation', - 'gender_limitation', - 'cost', - 'minimum_cost', - 'maximum_cost', - 'online_video_link', - 'online_video_meeting_id', - 'online_video_meeting_password', - # 'online_video_description', - ) - }), - ('Advanced options', { - 'classes': ('collapse',), - 'fields': ( - 'password', - 'partner_message', - 'additional_info', - 'external_enrollment_url', - 'image_url', - 'bg_image', - 'announced_date_mentors', - 'announced_date_guardians', - 'mentors_week_reminder_sent', - 'mentors_day_reminder_sent', - ), - }), + ( + None, + { + # 'classes': ('collapse',), + "fields": ( + "course", + "location", + "start_date", + # 'old_end_date', + # 'old_mentor_start_date', + # 'old_mentor_end_date', + "capacity", + "mentor_capacity", + "instructor", + "is_active", + "is_public", + ), + }, + ), + ( + "Overrides", + { + # 'classes': ('collapse',), + "fields": ( + "override_minimum_age_limitation", + "override_maximum_age_limitation", + "gender_limitation", + "cost", + "minimum_cost", + "maximum_cost", + "online_video_link", + "online_video_meeting_id", + "online_video_meeting_password", + # 'online_video_description', + ) + }, + ), + ( + "Advanced options", + { + "classes": ("collapse",), + "fields": ( + "password", + "partner_message", + "additional_info", + "external_enrollment_url", + "image_url", + "bg_image", + "announced_date_mentors", + "announced_date_guardians", + "mentors_week_reminder_sent", + "mentors_day_reminder_sent", + ), + }, + ), ) def view_on_site(self, obj): @@ -614,8 +575,9 @@ def view_on_site(self, obj): def _course(self, obj): return obj.course.code - _course.short_description = 'Course' - _course.admin_order_field = 'course__code' + + _course.short_description = "Course" + _course.admin_order_field = "course__code" def mentor_count_link(self, obj): return format_html( @@ -624,6 +586,7 @@ def mentor_count_link(self, obj): query=obj.id, count=MentorOrder.objects.filter(session__id=obj.id, is_active=True).count(), ) + mentor_count_link.short_description = "Mentors" mentor_count_link.admin_order_field = "mentor__count" @@ -634,6 +597,7 @@ def student_count_link(self, obj): query=obj.id, count=obj.get_current_orders().count(), ) + student_count_link.short_description = "Students" student_count_link.admin_order_field = "student__count" @@ -657,45 +621,45 @@ class OrderAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 50 list_display = [ - 'id', - 'get_student_link', - 'get_student_gender', - 'get_student_age', - 'get_guardian_link', - 'get_session_link', - '_created_at', - 'is_checked_in', - 'is_active', + "id", + "get_student_link", + "get_student_gender", + "get_student_age", + "get_guardian_link", + "get_session_link", + "_created_at", + "is_checked_in", + "is_active", ] list_filter = [ - 'is_active', - 'check_in', - 'session', + "is_active", + "check_in", + "session", ] list_select_related = [ - 'guardian', - 'session', - 'student', + "guardian", + "session", + "student", ] search_fields = [ - 'student__first_name', - 'student__last_name', + "student__first_name", + "student__last_name", ] ordering = [ - 'created_at', + "created_at", ] autocomplete_fields = [ - 'guardian', - 'session', - 'student', + "guardian", + "session", + "student", ] - date_hierarchy = 'created_at' + date_hierarchy = "created_at" view_on_site = False @@ -707,18 +671,20 @@ class OrderAdmin(ImportExportMixin, ImportExportActionModelAdmin): def get_student_link(self, obj): return format_html( '{student}', - url=reverse('admin:coderdojochi_student_change', args=(obj.student.id,)), + url=reverse("admin:coderdojochi_student_change", args=(obj.student.id,)), student=obj.student, ) - get_student_link.short_description = 'Student' + + get_student_link.short_description = "Student" def get_guardian_link(self, obj): return format_html( '{guardian}', - url=reverse('admin:coderdojochi_guardian_change', args=(obj.guardian.id,)), + url=reverse("admin:coderdojochi_guardian_change", args=(obj.guardian.id,)), guardian=obj.guardian, ) - get_guardian_link.short_description = 'Guardian' + + get_guardian_link.short_description = "Guardian" def get_session_link(self, obj): return format_html( @@ -726,11 +692,13 @@ def get_session_link(self, obj): url=reverse("admin:coderdojochi_session_change", args=(obj.session.course.id,)), course_code=obj.session.course.code, ) - get_session_link.short_description = 'Session' + + get_session_link.short_description = "Session" def _created_at(self, obj): return obj.created_at.strftime("%m/%d/%y %H:%M") - _created_at.short_description = 'Created At' + + _created_at.short_description = "Created At" def mentor_check_in(modeladmin, request, queryset): @@ -752,48 +720,48 @@ class MentorOrderAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 50 list_display = [ - 'mentor', - 'session', - 'ip', - 'is_checked_in', - 'is_active', - 'week_reminder_sent', - 'day_reminder_sent', - 'created_at', - 'updated_at', + "mentor", + "session", + "ip", + "is_checked_in", + "is_active", + "week_reminder_sent", + "day_reminder_sent", + "created_at", + "updated_at", ] list_display_links = [ - 'mentor', + "mentor", ] list_filter = [ - 'is_active', - 'check_in', - 'session', + "is_active", + "check_in", + "session", ] list_select_related = [ - 'mentor', - 'session', + "mentor", + "session", ] ordering = [ - 'created_at', + "created_at", ] search_fields = [ - 'mentor__user__first_name', - 'mentor__user__last_name', + "mentor__user__first_name", + "mentor__user__last_name", ] autocomplete_fields = [ - 'mentor', - 'session', + "mentor", + "session", ] readonly_fields = [ - 'ip', + "ip", ] actions = [ @@ -801,7 +769,7 @@ class MentorOrderAdmin(ImportExportMixin, ImportExportActionModelAdmin): mentor_check_out, ] - date_hierarchy = 'created_at' + date_hierarchy = "created_at" view_on_site = False @@ -824,41 +792,41 @@ class MeetingOrderAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 50 list_display = [ - 'mentor', - 'meeting', - 'ip', - 'is_checked_in', - 'is_active', - 'week_reminder_sent', - 'day_reminder_sent', - 'created_at', - 'updated_at', + "mentor", + "meeting", + "ip", + "is_checked_in", + "is_active", + "week_reminder_sent", + "day_reminder_sent", + "created_at", + "updated_at", ] list_filter = [ - 'is_active', - 'meeting', - 'check_in', - 'meeting__meeting_type', + "is_active", + "meeting", + "check_in", + "meeting__meeting_type", ] list_select_related = [ - 'mentor', - 'meeting', + "mentor", + "meeting", ] ordering = [ - 'created_at', + "created_at", ] search_fields = [ - 'mentor__user__first_name', - 'mentor__user__last_name', + "mentor__user__first_name", + "mentor__user__last_name", ] autocomplete_fields = [ - 'mentor', - 'meeting', + "mentor", + "meeting", ] actions = [ @@ -866,7 +834,7 @@ class MeetingOrderAdmin(ImportExportMixin, ImportExportActionModelAdmin): meeting_order_check_out, ] - date_hierarchy = 'created_at' + date_hierarchy = "created_at" view_on_site = False @@ -876,19 +844,24 @@ class MeetingOrderAdmin(ImportExportMixin, ImportExportActionModelAdmin): @admin.register(MeetingType) class MeetingTypeAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_display = [ - 'code', - 'title', - 'slug', + "code", + "title", + "slug", ] list_display_links = [ - 'title', + "title", ] search_fields = [ - 'code', - 'title', + "code", + "title", ] + + prepopulated_fields = { + "slug": ("title",), + } + view_on_site = False @@ -897,42 +870,40 @@ class MeetingAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_per_page = 10 list_display = [ - 'meeting_type', - 'start_date', - 'end_date', - 'location', - 'get_mentor_count', - 'is_public', - 'announced_date', - 'created_at', + "meeting_type", + "start_date", + "end_date", + "location", + "get_mentor_count", + "is_public", + "announced_date", + "created_at", ] list_filter = [ - 'is_active', - 'is_public', - 'location', - 'meeting_type__title', + "is_active", + "is_public", + "location", + "meeting_type__title", ] list_select_related = [ - 'meeting_type', - 'location', + "meeting_type", + "location", ] ordering = [ - '-start_date', + "-start_date", ] - search_fields = [ - 'start_date' - ] + search_fields = ["start_date"] autocomplete_fields = [ - 'meeting_type', - 'location', + "meeting_type", + "location", ] - date_hierarchy = 'start_date' + date_hierarchy = "start_date" def view_on_site(self, obj): return obj.get_absolute_url() @@ -946,42 +917,42 @@ class EquipmentTypeAdmin(ImportExportMixin, ImportExportActionModelAdmin): @admin.register(Equipment) class EquipmentAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_display = [ - 'uuid', - 'asset_tag', - 'equipment_type', - 'make', - 'model', - 'condition', - 'last_system_update_check_in', - 'last_system_update', - 'force_update_on_next_boot', + "uuid", + "asset_tag", + "equipment_type", + "make", + "model", + "condition", + "last_system_update_check_in", + "last_system_update", + "force_update_on_next_boot", ] list_filter = [ - 'condition', - 'equipment_type', - 'make', - 'model', + "condition", + "equipment_type", + "make", + "model", ] list_select_related = [ - 'equipment_type', + "equipment_type", ] ordering = [ - 'uuid', + "uuid", ] search_fields = [ - 'uuid', - 'make', - 'model', - 'asset_tag', + "uuid", + "make", + "model", + "asset_tag", ] readonly_fields = [ - 'last_system_update_check_in', - 'last_system_update', + "last_system_update_check_in", + "last_system_update", ] view_on_site = False @@ -990,41 +961,41 @@ class EquipmentAdmin(ImportExportMixin, ImportExportActionModelAdmin): @admin.register(Donation) class DonationAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_display = [ - 'session', - 'get_first_name', - 'get_last_name', - 'get_email', - 'amount', - 'is_verified', - 'receipt_sent', - 'created_at', + "session", + "get_first_name", + "get_last_name", + "get_email", + "amount", + "is_verified", + "receipt_sent", + "created_at", ] list_filter = [ - 'session', - 'user', - 'is_verified', - 'receipt_sent', - 'amount', - 'created_at', + "session", + "user", + "is_verified", + "receipt_sent", + "amount", + "created_at", ] ordering = [ - '-created_at', + "-created_at", ] search_fields = [ - 'get_first_name', - 'get_last_name', - 'get_email', - 'session', + "get_first_name", + "get_last_name", + "get_email", + "session", ] - date_hierarchy = 'created_at' + date_hierarchy = "created_at" autocomplete_fields = [ - 'user', - 'session', + "user", + "session", ] view_on_site = False @@ -1033,16 +1004,14 @@ class DonationAdmin(ImportExportMixin, ImportExportActionModelAdmin): @admin.register(Location) class LocationAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_display = [ - 'name', - 'address', - 'city', - 'state', - 'zip', + "name", + "address", + "city", + "state", + "zip", ] - search_fields = [ - 'name' - ] + search_fields = ["name"] view_on_site = False diff --git a/coderdojochi/apps.py b/coderdojochi/apps.py index fccb6250..4ed172aa 100644 --- a/coderdojochi/apps.py +++ b/coderdojochi/apps.py @@ -2,7 +2,7 @@ class CoderDojoChiConfig(AppConfig): - name = 'coderdojochi' + name = "coderdojochi" verbose_name = "CoderDojoChi" def ready(self): diff --git a/coderdojochi/context_processors.py b/coderdojochi/context_processors.py index 81897279..4d5bbe46 100644 --- a/coderdojochi/context_processors.py +++ b/coderdojochi/context_processors.py @@ -3,11 +3,11 @@ def main_config_processor(request): return { - 'DEBUG': settings.DEBUG, - 'SITE_URL': settings.SITE_URL, - 'STATIC_URL': settings.STATIC_URL, - 'CONTACT_EMAIL': settings.CONTACT_EMAIL, - 'SITE_PROTOCOL': settings.META_SITE_PROTOCOL, - 'SITE_DOMAIN': settings.META_SITE_DOMAIN, - 'SITE_NAME': settings.META_SITE_NAME, + "DEBUG": settings.DEBUG, + "SITE_URL": settings.SITE_URL, + "STATIC_URL": settings.STATIC_URL, + "CONTACT_EMAIL": settings.CONTACT_EMAIL, + "SITE_PROTOCOL": settings.META_SITE_PROTOCOL, + "SITE_DOMAIN": settings.META_SITE_DOMAIN, + "SITE_NAME": settings.META_SITE_NAME, } diff --git a/coderdojochi/cron.py b/coderdojochi/cron.py index 04b3bff0..14601931 100644 --- a/coderdojochi/cron.py +++ b/coderdojochi/cron.py @@ -1,7 +1,6 @@ from datetime import timedelta from django.conf import settings -from django.core.mail import get_connection from django.utils import timezone import arrow @@ -12,10 +11,10 @@ class SendReminders(CronJobBase): - RUN_AT_TIMES = ['10:00', '14:00'] + RUN_AT_TIMES = ["10:00", "14:00"] schedule = Schedule(run_at_times=RUN_AT_TIMES) - code = 'coderdojochi.send_reminders' + code = "coderdojochi.send_reminders" def do(self): orders_within_a_week = Order.objects.filter( @@ -50,38 +49,38 @@ def do(self): for order in orders_within_a_week: recipients.append(order.guardian.user.email) merge_data[order.guardian.user.email] = { - 'first_name': order.guardian.user.first_name, - 'last_name': order.guardian.user.last_name, - 'student_first_name': order.student.first_name, - 'student_last_name': order.student.last_name, - 'class_code': order.session.course.code, - 'class_title': order.session.course.title, - 'class_description': order.session.course.description, - 'class_start_date': arrow.get(order.session.start_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_start_time': arrow.get(order.session.start_date).to('local').format('h:mma'), - 'class_end_date': arrow.get(order.session.end_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_end_time': arrow.get(order.session.end_date).to('local').format('h:mma'), - 'class_location_name': order.session.location.name, - 'class_location_address': order.session.location.address, - 'class_location_city': order.session.location.city, - 'class_location_state': order.session.location.state, - 'class_location_zip': order.session.location.zip, - 'class_additional_info': order.session.additional_info, - 'class_url': f"{settings.SITE_URL}{order.session.get_absolute_url()}", - 'class_calendar_url': f"{settings.SITE_URL}{order.session.get_calendar_url()}", - 'microdata_start_date': arrow.get(order.session.start_date).to('local').isoformat(), - 'microdata_end_date': arrow.get(order.session.end_date).to('local').isoformat(), - 'order_id': order.id, - 'online_video_link': order.session.online_video_link, - 'online_video_description': order.session.online_video_description, + "first_name": order.guardian.user.first_name, + "last_name": order.guardian.user.last_name, + "student_first_name": order.student.first_name, + "student_last_name": order.student.last_name, + "class_code": order.session.course.code, + "class_title": order.session.course.title, + "class_description": order.session.course.description, + "class_start_date": arrow.get(order.session.start_date).to("local").format("dddd, MMMM D, YYYY"), + "class_start_time": arrow.get(order.session.start_date).to("local").format("h:mma"), + "class_end_date": arrow.get(order.session.end_date).to("local").format("dddd, MMMM D, YYYY"), + "class_end_time": arrow.get(order.session.end_date).to("local").format("h:mma"), + "class_location_name": order.session.location.name, + "class_location_address": order.session.location.address, + "class_location_city": order.session.location.city, + "class_location_state": order.session.location.state, + "class_location_zip": order.session.location.zip, + "class_additional_info": order.session.additional_info, + "class_url": f"{settings.SITE_URL}{order.session.get_absolute_url()}", + "class_calendar_url": f"{settings.SITE_URL}{order.session.get_calendar_url()}", + "microdata_start_date": arrow.get(order.session.start_date).to("local").isoformat(), + "microdata_end_date": arrow.get(order.session.end_date).to("local").isoformat(), + "order_id": order.id, + "online_video_link": order.session.online_video_link, + "online_video_description": order.session.online_video_description, } email( - subject='Upcoming class reminder', - template_name='class-reminder-guardian-one-week', + subject="Upcoming class reminder", + template_name="class-reminder-guardian-one-week", merge_data=merge_data, recipients=recipients, - preheader='Your class is just a few days away!', + preheader="Your class is just a few days away!", unsub_group_id=settings.SENDGRID_UNSUB_CLASSANNOUNCE, ) @@ -96,38 +95,38 @@ def do(self): for order in orders_within_a_day: recipients.append(order.guardian.user.email) merge_data[order.guardian.user.email] = { - 'first_name': order.guardian.user.first_name, - 'last_name': order.guardian.user.last_name, - 'student_first_name': order.student.first_name, - 'student_last_name': order.student.last_name, - 'class_code': order.session.course.code, - 'class_title': order.session.course.title, - 'class_description': order.session.course.description, - 'class_start_date': arrow.get(order.session.start_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_start_time': arrow.get(order.session.start_date).to('local').format('h:mma'), - 'class_end_date': arrow.get(order.session.end_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_end_time': arrow.get(order.session.end_date).to('local').format('h:mma'), - 'class_location_name': order.session.location.name, - 'class_location_address': order.session.location.address, - 'class_location_city': order.session.location.city, - 'class_location_state': order.session.location.state, - 'class_location_zip': order.session.location.zip, - 'class_additional_info': order.session.additional_info, - 'class_url': f"{settings.SITE_URL}{order.session.get_absolute_url()}", - 'class_calendar_url': f"{settings.SITE_URL}{order.session.get_calendar_url()}", - 'microdata_start_date': arrow.get(order.session.start_date).to('local').isoformat(), - 'microdata_end_date': arrow.get(order.session.end_date).to('local').isoformat(), - 'order_id': order.id, - 'online_video_link': order.session.online_video_link, - 'online_video_description': order.session.online_video_description, + "first_name": order.guardian.user.first_name, + "last_name": order.guardian.user.last_name, + "student_first_name": order.student.first_name, + "student_last_name": order.student.last_name, + "class_code": order.session.course.code, + "class_title": order.session.course.title, + "class_description": order.session.course.description, + "class_start_date": arrow.get(order.session.start_date).to("local").format("dddd, MMMM D, YYYY"), + "class_start_time": arrow.get(order.session.start_date).to("local").format("h:mma"), + "class_end_date": arrow.get(order.session.end_date).to("local").format("dddd, MMMM D, YYYY"), + "class_end_time": arrow.get(order.session.end_date).to("local").format("h:mma"), + "class_location_name": order.session.location.name, + "class_location_address": order.session.location.address, + "class_location_city": order.session.location.city, + "class_location_state": order.session.location.state, + "class_location_zip": order.session.location.zip, + "class_additional_info": order.session.additional_info, + "class_url": f"{settings.SITE_URL}{order.session.get_absolute_url()}", + "class_calendar_url": f"{settings.SITE_URL}{order.session.get_calendar_url()}", + "microdata_start_date": arrow.get(order.session.start_date).to("local").isoformat(), + "microdata_end_date": arrow.get(order.session.end_date).to("local").isoformat(), + "order_id": order.id, + "online_video_link": order.session.online_video_link, + "online_video_description": order.session.online_video_description, } email( - subject='Your class is coming up!', - template_name='class-reminder-guardian-24-hour', + subject="Your class is coming up!", + template_name="class-reminder-guardian-24-hour", merge_data=merge_data, recipients=recipients, - preheader='Your class is just hours away!', + preheader="Your class is just hours away!", unsub_group_id=settings.SENDGRID_UNSUB_CLASSANNOUNCE, ) @@ -145,38 +144,38 @@ def do(self): for order in orders: recipients.append(order.mentor.user.email) merge_data[order.mentor.user.email] = { - 'first_name': order.mentor.user.first_name, - 'last_name': order.mentor.user.last_name, - 'class_code': order.session.course.code, - 'class_title': order.session.course.title, - 'class_description': order.session.course.description, - 'class_start_date': arrow.get( - order.session.mentor_start_date - ).to('local').format('dddd, MMMM D, YYYY'), - 'class_start_time': arrow.get(order.session.mentor_start_date).to('local').format('h:mma'), - 'class_end_date': arrow.get(order.session.mentor_end_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_end_time': arrow.get(order.session.mentor_end_date).to('local').format('h:mma'), - 'class_location_name': order.session.location.name, - 'class_location_address': order.session.location.address, - 'class_location_city': order.session.location.city, - 'class_location_state': order.session.location.state, - 'class_location_zip': order.session.location.zip, - 'class_additional_info': order.session.additional_info, - 'class_url': f"{settings.SITE_URL}{order.session.get_absolute_url()}", - 'class_calendar_url': f"{settings.SITE_URL}{order.session.get_calendar_url()}", - 'microdata_start_date': arrow.get(order.session.start_date).to('local').isoformat(), - 'microdata_end_date': arrow.get(order.session.end_date).to('local').isoformat(), - 'order_id': order.id, - 'online_video_link': order.session.online_video_link, - 'online_video_description': order.session.online_video_description, + "first_name": order.mentor.user.first_name, + "last_name": order.mentor.user.last_name, + "class_code": order.session.course.code, + "class_title": order.session.course.title, + "class_description": order.session.course.description, + "class_start_date": ( + arrow.get(order.session.mentor_start_date).to("local").format("dddd, MMMM D, YYYY") + ), + "class_start_time": arrow.get(order.session.mentor_start_date).to("local").format("h:mma"), + "class_end_date": arrow.get(order.session.mentor_end_date).to("local").format("dddd, MMMM D, YYYY"), + "class_end_time": arrow.get(order.session.mentor_end_date).to("local").format("h:mma"), + "class_location_name": order.session.location.name, + "class_location_address": order.session.location.address, + "class_location_city": order.session.location.city, + "class_location_state": order.session.location.state, + "class_location_zip": order.session.location.zip, + "class_additional_info": order.session.additional_info, + "class_url": f"{settings.SITE_URL}{order.session.get_absolute_url()}", + "class_calendar_url": f"{settings.SITE_URL}{order.session.get_calendar_url()}", + "microdata_start_date": arrow.get(order.session.start_date).to("local").isoformat(), + "microdata_end_date": arrow.get(order.session.end_date).to("local").isoformat(), + "order_id": order.id, + "online_video_link": order.session.online_video_link, + "online_video_description": order.session.online_video_description, } email( - subject='Your We All Code class is in less than a week!', - template_name='class-reminder-mentor-one-week', + subject="Your We All Code class is in less than a week!", + template_name="class-reminder-mentor-one-week", merge_data=merge_data, recipients=recipients, - preheader='The class is just a few days away!', + preheader="The class is just a few days away!", unsub_group_id=settings.SENDGRID_UNSUB_CLASSANNOUNCE, ) @@ -189,38 +188,38 @@ def do(self): for order in orders: recipients.append(order.mentor.user.email) merge_data[order.mentor.user.email] = { - 'first_name': order.mentor.user.first_name, - 'last_name': order.mentor.user.last_name, - 'class_code': order.session.course.code, - 'class_title': order.session.course.title, - 'class_description': order.session.course.description, - 'class_start_date': arrow.get( - order.session.mentor_start_date - ).to('local').format('dddd, MMMM D, YYYY'), - 'class_start_time': arrow.get(order.session.mentor_start_date).to('local').format('h:mma'), - 'class_end_date': arrow.get(order.session.mentor_end_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_end_time': arrow.get(order.session.mentor_end_date).to('local').format('h:mma'), - 'class_location_name': order.session.location.name, - 'class_location_address': order.session.location.address, - 'class_location_city': order.session.location.city, - 'class_location_state': order.session.location.state, - 'class_location_zip': order.session.location.zip, - 'class_additional_info': order.session.additional_info, - 'class_url': f"{settings.SITE_URL}{order.session.get_absolute_url()}", - 'class_calendar_url': f"{settings.SITE_URL}{order.session.get_calendar_url()}", - 'microdata_start_date': arrow.get(order.session.start_date).to('local').isoformat(), - 'microdata_end_date': arrow.get(order.session.end_date).to('local').isoformat(), - 'order_id': order.id, - 'online_video_link': order.session.online_video_link, - 'online_video_description': order.session.online_video_description, + "first_name": order.mentor.user.first_name, + "last_name": order.mentor.user.last_name, + "class_code": order.session.course.code, + "class_title": order.session.course.title, + "class_description": order.session.course.description, + "class_start_date": ( + arrow.get(order.session.mentor_start_date).to("local").format("dddd, MMMM D, YYYY") + ), + "class_start_time": arrow.get(order.session.mentor_start_date).to("local").format("h:mma"), + "class_end_date": arrow.get(order.session.mentor_end_date).to("local").format("dddd, MMMM D, YYYY"), + "class_end_time": arrow.get(order.session.mentor_end_date).to("local").format("h:mma"), + "class_location_name": order.session.location.name, + "class_location_address": order.session.location.address, + "class_location_city": order.session.location.city, + "class_location_state": order.session.location.state, + "class_location_zip": order.session.location.zip, + "class_additional_info": order.session.additional_info, + "class_url": f"{settings.SITE_URL}{order.session.get_absolute_url()}", + "class_calendar_url": f"{settings.SITE_URL}{order.session.get_calendar_url()}", + "microdata_start_date": arrow.get(order.session.start_date).to("local").isoformat(), + "microdata_end_date": arrow.get(order.session.end_date).to("local").isoformat(), + "order_id": order.id, + "online_video_link": order.session.online_video_link, + "online_video_description": order.session.online_video_description, } email( - subject='Your We All Code class is tomorrow!', - template_name='class-reminder-mentor-24-hour', + subject="Your We All Code class is tomorrow!", + template_name="class-reminder-mentor-24-hour", merge_data=merge_data, recipients=recipients, - preheader='The class is just a few hours away!', + preheader="The class is just a few hours away!", unsub_group_id=settings.SENDGRID_UNSUB_CLASSANNOUNCE, ) diff --git a/coderdojochi/factories.py b/coderdojochi/factories.py index f4b53c63..196885a7 100644 --- a/coderdojochi/factories.py +++ b/coderdojochi/factories.py @@ -17,9 +17,9 @@ class Meta: class LocationFactory(factory.DjangoModelFactory): name = factory.Sequence(lambda n: f"Test Location {n}") address = factory.Sequence(lambda n: f"{n} Street") - city = 'Chicago' - state = 'IL' - zip = '60605' + city = "Chicago" + state = "IL" + zip = "60605" class Meta: model = Location @@ -47,7 +47,7 @@ class SessionFactory(factory.DjangoModelFactory): end_date = datetime.now(utc) mentor_start_date = datetime.now(utc) mentor_end_date = datetime.now(utc) - password = '' + password = "" instructor = factory.SubFactory(MentorFactory) class Meta: diff --git a/coderdojochi/forms.py b/coderdojochi/forms.py index ab92d16e..4e98297d 100644 --- a/coderdojochi/forms.py +++ b/coderdojochi/forms.py @@ -1,5 +1,4 @@ import re -from datetime import timedelta from django import forms from django.contrib.auth import get_user_model @@ -12,15 +11,7 @@ import html5.forms.widgets as html5_widgets from dateutil.relativedelta import relativedelta -from coderdojochi.models import ( - CDCUser, - Donation, - Guardian, - Mentor, - RaceEthnicity, - Session, - Student, -) +from coderdojochi.models import CDCUser, Donation, Guardian, Mentor, RaceEthnicity, Session, Student class CDCForm(Form): @@ -30,11 +21,7 @@ def _clean_fields(self): # value_from_datadict() gets the data from the data dictionaries. # Each widget type knows how to retrieve its own data, because some # widgets split data over several HTML fields. - value = field.widget.value_from_datadict( - self.data, - self.files, - self.add_prefix(name) - ) + value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) try: if isinstance(field, FileField): @@ -63,11 +50,7 @@ def _clean_fields(self): # value_from_datadict() gets the data from the data dictionaries. # Each widget type knows how to retrieve its own data, because some # widgets split data over several HTML fields. - value = field.widget.value_from_datadict( - self.data, - self.files, - self.add_prefix(name) - ) + value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) try: if isinstance(field, FileField): @@ -77,8 +60,8 @@ def _clean_fields(self): if isinstance(value, str): # regex normalizes carriage return # and cuts them to two at most - value = re.sub(r'\r\n', '\n', value) - value = re.sub(r'\n{3,}', '\n\n', value) + value = re.sub(r"\r\n", "\n", value) + value = re.sub(r"\n{3,}", "\n\n", value) value = field.clean(value.strip()) else: value = field.clean(value) @@ -94,7 +77,7 @@ def _clean_fields(self): class Meta: model = CDCUser - fields = ('first_name', 'last_name') + fields = ("first_name", "last_name") class SignupForm(forms.Form): @@ -105,89 +88,46 @@ class Meta: model = get_user_model() def save(self, user): - user.first_name = self.cleaned_data['first_name'] - user.last_name = self.cleaned_data['last_name'] + user.first_name = self.cleaned_data["first_name"] + user.last_name = self.cleaned_data["last_name"] user.save() class MentorForm(CDCModelForm): bio = forms.CharField( - widget=forms.Textarea( - attrs={ - 'placeholder': 'Short Bio', - 'class': 'form-control', - 'rows': 4 - } - ), - label='Short Bio', - required=False + widget=forms.Textarea(attrs={"placeholder": "Short Bio", "class": "form-control", "rows": 4}), + label="Short Bio", + required=False, ) gender = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': '', - 'class': 'form-control' - } - ), - label='Gender', - required=True + widget=forms.TextInput(attrs={"placeholder": "", "class": "form-control"}), label="Gender", required=True ) race_ethnicity = forms.ModelMultipleChoiceField( - widget=forms.SelectMultiple, - queryset=RaceEthnicity.objects.filter(is_visible=True), - required=True + widget=forms.SelectMultiple, queryset=RaceEthnicity.objects.filter(is_visible=True), required=True ) - birthday = forms.CharField( - widget=html5_widgets.DateInput( - attrs={ - 'class': 'form-control' - } - ), - required=True - ) + birthday = forms.CharField(widget=html5_widgets.DateInput(attrs={"class": "form-control"}), required=True) work_place = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': '', - 'class': 'form-control' - } - ), - label='Work Place', - required=False + widget=forms.TextInput(attrs={"placeholder": "", "class": "form-control"}), label="Work Place", required=False ) phone = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': '', - 'class': 'form-control' - } - ), - label='Phone', - required=False + widget=forms.TextInput(attrs={"placeholder": "", "class": "form-control"}), label="Phone", required=False ) home_address = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': '', - 'class': 'form-control' - } - ), - label='Home Address', - required=False + widget=forms.TextInput(attrs={"placeholder": "", "class": "form-control"}), label="Home Address", required=False ) class Meta: model = Mentor - fields = ('bio', 'avatar', 'gender', 'race_ethnicity', 'birthday', 'phone', 'home_address', 'work_place') + fields = ("bio", "avatar", "gender", "race_ethnicity", "birthday", "phone", "home_address", "work_place") def clean_avatar(self): - avatar = self.cleaned_data['avatar'] + avatar = self.cleaned_data["avatar"] if avatar is None: return avatar @@ -198,102 +138,60 @@ def clean_avatar(self): # validate dimensions max_width = max_height = 1000 if w > max_width or h > max_height: - raise forms.ValidationError( - f'Please use an image that is {max_width} x {max_height}px or smaller.' - ) + raise forms.ValidationError(f"Please use an image that is {max_width} x {max_height}px or smaller.") min_width = min_height = 500 if w < min_width or h < min_height: - raise forms.ValidationError( - f'Please use an image that is {min_width} x {min_height}px or larger.' - ) + raise forms.ValidationError(f"Please use an image that is {min_width} x {min_height}px or larger.") # validate content type - main, sub = avatar.content_type.split('/') - if ( - not ( - main == 'image' and - sub in ['jpeg', 'pjpeg', 'gif', 'png'] - ) - ): - raise forms.ValidationError( - 'Please use a JPEG, GIF or PNG image.' - ) + main, sub = avatar.content_type.split("/") + if not (main == "image" and sub in ["jpeg", "pjpeg", "gif", "png"]): + raise forms.ValidationError("Please use a JPEG, GIF or PNG image.") # validate file size if len(avatar) > (2000 * 1024): - raise forms.ValidationError( - 'Avatar file size may not exceed 2MB.' - ) + raise forms.ValidationError("Avatar file size may not exceed 2MB.") except AttributeError: """ Handles case when we are updating the user profile and do not supply a new avatar """ - pass return avatar class GuardianForm(CDCModelForm): phone = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': 'Phone Number', - 'class': 'form-control' - } - ), - label='Phone Number' + widget=forms.TextInput(attrs={"placeholder": "Phone Number", "class": "form-control"}), label="Phone Number" ) zip = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': 'Zip Code', - 'class': 'form-control' - } - ), - label='Zip Code' + widget=forms.TextInput(attrs={"placeholder": "Zip Code", "class": "form-control"}), label="Zip Code" ) gender = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': '', - 'class': 'form-control' - } - ), - label='Gender', - required=True + widget=forms.TextInput(attrs={"placeholder": "", "class": "form-control"}), label="Gender", required=True ) race_ethnicity = forms.ModelMultipleChoiceField( - widget=forms.SelectMultiple, - queryset=RaceEthnicity.objects.filter(is_visible=True), - required=True + widget=forms.SelectMultiple, queryset=RaceEthnicity.objects.filter(is_visible=True), required=True ) - birthday = forms.CharField( - widget=html5_widgets.DateInput( - attrs={ - 'class': 'form-control' - } - ), - required=True - ) + birthday = forms.CharField(widget=html5_widgets.DateInput(attrs={"class": "form-control"}), required=True) class Meta: model = Guardian - fields = ('phone', 'zip', 'gender', 'race_ethnicity', 'birthday') + fields = ("phone", "zip", "gender", "race_ethnicity", "birthday") class StudentForm(CDCModelForm): - PUBLIC = 'PU' - CHARTER = 'CH' - PRIVATE = 'PR' - HOMESCHOOL = 'HM' + PUBLIC = "PU" + CHARTER = "CH" + PRIVATE = "PR" + HOMESCHOOL = "HM" SCHOOL_TYPE_CHOICES = [ (PUBLIC, "Public"), @@ -303,63 +201,33 @@ class StudentForm(CDCModelForm): ] first_name = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': 'Jane', - 'class': 'form-control', - }, - ), - label='First Name', + widget=forms.TextInput(attrs={"placeholder": "Jane", "class": "form-control",},), label="First Name", ) last_name = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': 'Doe', - 'class': 'form-control', - }, - ), - label='Last Name', + widget=forms.TextInput(attrs={"placeholder": "Doe", "class": "form-control",},), label="Last Name", ) gender = forms.CharField( - widget=forms.TextInput( - attrs={ - 'placeholder': '', - 'class': 'form-control', - }, - ), - label='Gender', + widget=forms.TextInput(attrs={"placeholder": "", "class": "form-control",},), label="Gender", ) school_name = forms.CharField( - widget=forms.TextInput( - attrs={ - 'class': 'form-control', - }, - ), - label='School Name', - required=False, + widget=forms.TextInput(attrs={"class": "form-control",},), label="School Name", required=False, ) - school_type = forms.ChoiceField( - widget=forms.RadioSelect, - choices=SCHOOL_TYPE_CHOICES, - required=False, - ) + school_type = forms.ChoiceField(widget=forms.RadioSelect, choices=SCHOOL_TYPE_CHOICES, required=False,) race_ethnicity = forms.ModelMultipleChoiceField( - widget=forms.CheckboxSelectMultiple, - queryset=RaceEthnicity.objects.filter(is_visible=True), - required=False, + widget=forms.CheckboxSelectMultiple, queryset=RaceEthnicity.objects.filter(is_visible=True), required=False, ) birthday = forms.CharField( widget=html5_widgets.DateInput( attrs={ - 'class': 'form-control', - 'min': dateformat.format(timezone.now() - relativedelta(years=18), "Y-m-d"), - 'max': dateformat.format(timezone.now() - relativedelta(years=5), "Y-m-d"), + "class": "form-control", + "min": dateformat.format(timezone.now() - relativedelta(years=18), "Y-m-d"), + "max": dateformat.format(timezone.now() - relativedelta(years=5), "Y-m-d"), } ), ) @@ -367,87 +235,64 @@ class StudentForm(CDCModelForm): medications = forms.CharField( widget=forms.Textarea( attrs={ - 'placeholder': 'List any medications currently being taken.', - 'class': 'form-control hidden', - 'rows': 5, + "placeholder": "List any medications currently being taken.", + "class": "form-control hidden", + "rows": 5, } ), label=format_html( "{0} {1}", "Medications", - mark_safe( - 'expand' - ), + mark_safe('expand'), ), required=False, ) medical_conditions = forms.CharField( widget=forms.Textarea( - attrs={ - 'placeholder': 'List any medical conditions.', - 'class': 'form-control hidden', - 'rows': 5, - }, + attrs={"placeholder": "List any medical conditions.", "class": "form-control hidden", "rows": 5,}, ), label=format_html( "{0} {1}", "Medical Conditions", - mark_safe( - 'expand' - ), + mark_safe('expand'), ), required=False, ) photo_release = forms.BooleanField( - widget=forms.CheckboxInput( - attrs={ - 'required': 'required', - }, - ), + widget=forms.CheckboxInput(attrs={"required": "required",},), label=( - 'I hereby give permission to We All Code to use the ' - 'student\'s image and/or likeness in promotional materials.' + "I hereby give permission to We All Code to use the " + "student's image and/or likeness in promotional materials." ), ) consent = forms.BooleanField( - widget=forms.CheckboxInput( - attrs={ - 'required': 'required', - }, - ), + widget=forms.CheckboxInput(attrs={"required": "required",},), label=( - 'I hereby give consent for the student signed up above to participate in We All Code as per the ' + "I hereby give consent for the student signed up above to participate in We All Code as per the " f"terms." ), ) class Meta: model = Student - exclude = ('guardian', 'created_at', 'updated_at', 'is_active') + exclude = ("guardian", "created_at", "updated_at", "is_active") class ContactForm(CDCForm): - name = forms.CharField(max_length=100, label='Your name') - email = forms.EmailField(max_length=200, label='Your email address') - message = forms.CharField(widget=forms.Textarea, label='Your message') + name = forms.CharField(max_length=100, label="Your name") + email = forms.EmailField(max_length=200, label="Your email address") + message = forms.CharField(widget=forms.Textarea, label="Your message") human = forms.CharField(max_length=100, label=False, required=False) class DonationForm(ModelForm): - session = forms.ModelChoiceField( - queryset=Session.objects.all(), - widget=forms.HiddenInput(), - required=True - ) - user = forms.ModelChoiceField( - queryset=CDCUser.objects.all(), - required=True - ) - amount = forms.CharField(label='Amount (dollars)') + session = forms.ModelChoiceField(queryset=Session.objects.all(), widget=forms.HiddenInput(), required=True) + user = forms.ModelChoiceField(queryset=CDCUser.objects.all(), required=True) + amount = forms.CharField(label="Amount (dollars)") class Meta: model = Donation - fields = ['session', 'user', 'amount'] + fields = ["session", "user", "amount"] diff --git a/coderdojochi/middleware.py b/coderdojochi/middleware.py index fe3d6d4a..67f5699d 100644 --- a/coderdojochi/middleware.py +++ b/coderdojochi/middleware.py @@ -22,7 +22,6 @@ def process_exception(self, request, exception): if settings.DEBUG: raise exception - return render(request, "500.html", { - 'sentry_event_id': last_event_id(), - 'SENTRY_DSN': settings.SENTRY_DSN, - }, status=500) + return render( + request, "500.html", {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, status=500 + ) diff --git a/coderdojochi/migrations/0036_auto_20200812_1246.py b/coderdojochi/migrations/0036_auto_20200812_1246.py new file mode 100644 index 00000000..f8cfcfc3 --- /dev/null +++ b/coderdojochi/migrations/0036_auto_20200812_1246.py @@ -0,0 +1,61 @@ +# Generated by Django 3.0.7 on 2020-08-12 17:46 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('coderdojochi', '0035_auto_20200811_2046'), + ] + + operations = [ + migrations.AlterModelOptions( + name='course', + options={}, + ), + migrations.AlterModelOptions( + name='donation', + options={}, + ), + migrations.AlterModelOptions( + name='guardian', + options={}, + ), + migrations.AlterModelOptions( + name='meeting', + options={}, + ), + migrations.AlterModelOptions( + name='meetingorder', + options={}, + ), + migrations.AlterModelOptions( + name='meetingtype', + options={}, + ), + migrations.AlterModelOptions( + name='mentor', + options={}, + ), + migrations.AlterModelOptions( + name='mentororder', + options={}, + ), + migrations.AlterModelOptions( + name='order', + options={}, + ), + migrations.AlterModelOptions( + name='partnerpasswordaccess', + options={}, + ), + migrations.AlterModelOptions( + name='session', + options={}, + ), + migrations.AlterModelOptions( + name='student', + options={}, + ), + ] diff --git a/coderdojochi/mixins.py b/coderdojochi/mixins.py index 3ca9597c..6a52b23c 100644 --- a/coderdojochi/mixins.py +++ b/coderdojochi/mixins.py @@ -1,18 +1,14 @@ -import logging - from django.contrib import messages -from django.shortcuts import get_object_or_404, redirect +from django.shortcuts import redirect from django.urls import reverse -from coderdojochi.models import Session - class RoleTemplateMixin(object): def get_template_names(self): if self.request.user.is_authenticated: - if self.request.user.role == 'mentor': + if self.request.user.role == "mentor": template_name = f"mentor/{self.template_name}" - elif self.request.user.role == 'guardian': + elif self.request.user.role == "guardian": template_name = f"guardian/{self.template_name}" else: template_name = self.template_name @@ -21,18 +17,17 @@ def get_template_names(self): class RoleRedirectMixin(object): - def dispatch(self, request, *args, **kwargs): - session_obj = kwargs.get('session_obj') + session_obj = kwargs.get("session_obj") user = request.user if user.is_authenticated and session_obj and not user.role: - messages.warning(request, 'Please select one of the following options to continue.') + messages.warning(request, "Please select one of the following options to continue.") next_url = f"{reverse('welcome')}?next={session_obj.get_absolute_url()}" - if 'enroll' in request.GET: - next_url += '&enroll=True' + if "enroll" in request.GET: + next_url += "&enroll=True" return redirect(next_url) diff --git a/coderdojochi/models.py b/coderdojochi/models.py index 0b395ad3..7ff45757 100644 --- a/coderdojochi/models.py +++ b/coderdojochi/models.py @@ -5,42 +5,28 @@ from django.contrib.contenttypes.models import ContentType from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models -from django.template.defaultfilters import slugify from django.urls import reverse from django.utils import formats, timezone from django.utils.functional import cached_property -from django.utils.translation import ugettext as _ from stdimage.models import StdImageField -from .notifications import ( - NewMentorBgCheckNotification, - NewMentorNotification, - NewMentorOrderNotification, -) +from .notifications import NewMentorBgCheckNotification, NewMentorNotification, NewMentorOrderNotification class CDCUser(AbstractUser): - MENTOR = 'mentor' - GUARDIAN = 'guardian' + MENTOR = "mentor" + GUARDIAN = "guardian" ROLE_CHOICES = [ - (MENTOR, 'mentor'), - (GUARDIAN, 'guardian'), + (MENTOR, "mentor"), + (GUARDIAN, "guardian"), ] - role = models.CharField( - choices=ROLE_CHOICES, - max_length=10, - blank=True, - null=True, - ) + role = models.CharField(choices=ROLE_CHOICES, max_length=10, blank=True, null=True,) - admin_notes = models.TextField( - blank=True, - null=True, - ) + admin_notes = models.TextField(blank=True, null=True,) @cached_property def name(self): @@ -52,7 +38,7 @@ def save(self, *args, **kwargs): super(CDCUser, self).save(*args, **kwargs) def get_absolute_url(self): - return reverse('account_home') + return reverse("account_home") def generate_filename(instance, filename): @@ -63,30 +49,22 @@ def generate_filename(instance, filename): class CommonInfo(models.Model): # Auto create/update - created_at = models.DateTimeField( - auto_now_add=True, - ) + created_at = models.DateTimeField(auto_now_add=True,) - updated_at = models.DateTimeField( - auto_now=True, - ) + updated_at = models.DateTimeField(auto_now=True,) class Meta: abstract = True class RaceEthnicity(CommonInfo): - race_ethnicity = models.CharField( - max_length=255, - ) + race_ethnicity = models.CharField(max_length=255,) - is_visible = models.BooleanField( - default=False, - ) + is_visible = models.BooleanField(default=False,) class Meta: - verbose_name = _("Race/Ethnicity") - verbose_name_plural = _("Race/Ethnicities") + verbose_name = "Race/Ethnicity" + verbose_name_plural = "Race/Ethnicities" def __str__(self): return self.race_ethnicity @@ -94,81 +72,33 @@ def __str__(self): class Mentor(CommonInfo): - user = models.ForeignKey( - CDCUser, - on_delete=models.CASCADE, - ) + user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - bio = models.TextField( - blank=True, - null=True, - ) + bio = models.TextField(blank=True, null=True,) - is_active = models.BooleanField( - default=True, - ) + is_active = models.BooleanField(default=True,) - background_check = models.BooleanField( - default=False, - ) + background_check = models.BooleanField(default=False,) - is_public = models.BooleanField( - default=False, - ) + is_public = models.BooleanField(default=False,) avatar = StdImageField( - upload_to=generate_filename, - blank=True, - variations={ - 'thumbnail': { - 'width': 500, - 'height': 500, - 'crop': True, - } - }, + upload_to=generate_filename, blank=True, variations={"thumbnail": {"width": 500, "height": 500, "crop": True,}}, ) - avatar_approved = models.BooleanField( - default=False, - ) + avatar_approved = models.BooleanField(default=False,) - birthday = models.DateTimeField( - blank=False, - null=True, - ) + birthday = models.DateTimeField(blank=False, null=True,) - gender = models.CharField( - max_length=255, - blank=False, - null=True, - ) + gender = models.CharField(max_length=255, blank=False, null=True,) - race_ethnicity = models.ManyToManyField( - RaceEthnicity, - blank=False, - ) + race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=False,) - work_place = models.CharField( - max_length=255, - blank=True, - null=True, - ) - - phone = models.CharField( - max_length=255, - blank=True, - null=True, - ) + work_place = models.CharField(max_length=255, blank=True, null=True,) - home_address = models.CharField( - max_length=255, - blank=True, - null=True, - ) + phone = models.CharField(max_length=255, blank=True, null=True,) - class Meta: - verbose_name = _("mentors") - verbose_name_plural = _("mentors") + home_address = models.CharField(max_length=255, blank=True, null=True,) def __str__(self): return f"{self.user.first_name} {self.user.last_name}" @@ -190,13 +120,13 @@ def save(self, *args, **kwargs): super(Mentor, self).save(*args, **kwargs) def get_approve_avatar_url(self): - return reverse('mentor-approve-avatar', args=[str(self.id)]) + return reverse("mentor-approve-avatar", args=[str(self.id)]) def get_reject_avatar_url(self): - return reverse('mentor-reject-avatar', args=[str(self.id)]) + return reverse("mentor-reject-avatar", args=[str(self.id)]) def get_absolute_url(self): - return reverse('mentor-detail', args=[str(self.id)]) + return reverse("mentor-detail", args=[str(self.id)]) @property def first_name(self): @@ -208,45 +138,19 @@ def last_name(self): class Guardian(CommonInfo): - user = models.ForeignKey( - CDCUser, - on_delete=models.CASCADE, - ) + user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - is_active = models.BooleanField( - default=True, - ) + is_active = models.BooleanField(default=True,) - phone = models.CharField( - max_length=50, - blank=True, - ) + phone = models.CharField(max_length=50, blank=True,) - zip = models.CharField( - max_length=20, - blank=True, - null=True, - ) + zip = models.CharField(max_length=20, blank=True, null=True,) - birthday = models.DateTimeField( - blank=False, - null=True, - ) + birthday = models.DateTimeField(blank=False, null=True,) - gender = models.CharField( - max_length=255, - blank=False, - null=True, - ) + gender = models.CharField(max_length=255, blank=False, null=True,) - race_ethnicity = models.ManyToManyField( - RaceEthnicity, - blank=False, - ) - - class Meta: - verbose_name = _("guardian") - verbose_name_plural = _("guardians") + race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=False,) def __str__(self): return f"{self.user.first_name} {self.user.last_name}" @@ -264,82 +168,46 @@ def email(self): return self.user.email def get_students(self): - return Student.objects.filter( - guardian=self, - is_active=True, - ) + return Student.objects.filter(guardian=self, is_active=True,) class Student(CommonInfo): - guardian = models.ForeignKey( - Guardian, - on_delete=models.CASCADE, - ) + guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE,) - first_name = models.CharField( - max_length=255, - ) + first_name = models.CharField(max_length=255,) - last_name = models.CharField( - max_length=255, - ) + last_name = models.CharField(max_length=255,) birthday = models.DateTimeField() - gender = models.CharField( - max_length=255, - ) + gender = models.CharField(max_length=255,) - race_ethnicity = models.ManyToManyField( - RaceEthnicity, - blank=True, - ) + race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=True,) - school_name = models.CharField( - max_length=255, - null=True, - ) + school_name = models.CharField(max_length=255, null=True,) - school_type = models.CharField( - max_length=255, - null=True, - ) + school_type = models.CharField(max_length=255, null=True,) - medical_conditions = models.TextField( - blank=True, - null=True, - ) + medical_conditions = models.TextField(blank=True, null=True,) - medications = models.TextField( - blank=True, - null=True, - ) + medications = models.TextField(blank=True, null=True,) photo_release = models.BooleanField( - 'Photo Consent', + "Photo Consent", help_text=( - 'I hereby give permission to We All Code to use ' - 'the student\'s image and/or likeness in promotional materials.' + "I hereby give permission to We All Code to use " + "the student's image and/or likeness in promotional materials." ), default=False, ) consent = models.BooleanField( - 'General Consent', - help_text=( - 'I hereby give consent for the student signed up ' - 'above to participate in We All Code.' - ), + "General Consent", + help_text=("I hereby give consent for the student signed up " "above to participate in We All Code."), default=False, ) - is_active = models.BooleanField( - default=True, - ) - - class Meta: - verbose_name = _("student") - verbose_name_plural = _("students") + is_active = models.BooleanField(default=True,) def __str__(self): return f"{self.first_name} {self.last_name}" @@ -347,9 +215,7 @@ def __str__(self): def is_registered_for_session(self, session): try: Order.objects.get( - is_active=True, - student=self, - session=session, + is_active=True, student=self, session=session, ) is_registered = True except Exception: @@ -358,22 +224,22 @@ def is_registered_for_session(self, session): return is_registered def get_age(self, date=timezone.now()): - return date.year - self.birthday.year - ( - (date.month, date.day) < (self.birthday.month, self.birthday.day) - ) - get_age.short_description = 'Age' + return date.year - self.birthday.year - ((date.month, date.day) < (self.birthday.month, self.birthday.day)) + + get_age.short_description = "Age" def get_clean_gender(self): - MALE = ['male', 'm', 'boy', 'nino', 'masculino'] - FEMALE = ['female', 'f', 'girl', 'femail', 'femal', 'femenino'] + MALE = ["male", "m", "boy", "nino", "masculino"] + FEMALE = ["female", "f", "girl", "femail", "femal", "femenino"] if self.gender.lower() in MALE: - return 'male' + return "male" elif self.gender.lower() in FEMALE: - return 'female' + return "female" else: - return 'other' - get_clean_gender.short_description = 'Clean Gender' + return "other" + + get_clean_gender.short_description = "Clean Gender" # returns True if the student age is between minimum_age and maximum_age def is_within_age_range(self, minimum_age, maximum_age, date=timezone.now()): @@ -386,7 +252,7 @@ def is_within_age_range(self, minimum_age, maximum_age, date=timezone.now()): def is_within_gender_limitation(self, limitation): if limitation: - if self.get_clean_gender() in [limitation.lower(), 'other']: + if self.get_clean_gender() in [limitation.lower(), "other"]: return True else: return False @@ -395,73 +261,33 @@ def is_within_gender_limitation(self, limitation): class Course(CommonInfo): - WEEKEND = 'WE' - CAMP = 'CA' - SPECIAL = 'SP' + WEEKEND = "WE" + CAMP = "CA" + SPECIAL = "SP" COURSE_TYPE_CHOICES = [ - (WEEKEND, 'Weekend'), - (CAMP, 'Camp'), - (SPECIAL, 'Special'), + (WEEKEND, "Weekend"), + (CAMP, "Camp"), + (SPECIAL, "Special"), ] - code = models.CharField( - max_length=255, - blank=True, - null=True, - ) + code = models.CharField(max_length=255, blank=True, null=True,) - course_type = models.CharField( - 'type', - max_length=2, - choices=COURSE_TYPE_CHOICES, - default=WEEKEND, - ) + course_type = models.CharField("type", max_length=2, choices=COURSE_TYPE_CHOICES, default=WEEKEND,) - title = models.CharField( - max_length=255, - ) + title = models.CharField(max_length=255,) - slug = models.SlugField( - max_length=40, - blank=True, - null=True, - ) - - description = models.TextField( - blank=True, - null=True, - help_text="Basic HTML allowed", - ) + slug = models.SlugField(max_length=40, blank=True, null=True,) - duration = models.DurationField( - default=timedelta(hours=3), - help_text="HH:MM:ss", - ) + description = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - minimum_age = models.IntegerField( - default=7, - validators=[ - MinValueValidator(0), - MaxValueValidator(100) - ], - ) + duration = models.DurationField(default=timedelta(hours=3), help_text="HH:MM:ss",) - maximum_age = models.IntegerField( - default=17, - validators=[ - MinValueValidator(0), - MaxValueValidator(100) - ], - ) + minimum_age = models.IntegerField(default=7, validators=[MinValueValidator(0), MaxValueValidator(100)],) - is_active = models.BooleanField( - default=True, - ) + maximum_age = models.IntegerField(default=17, validators=[MinValueValidator(0), MaxValueValidator(100)],) - class Meta: - verbose_name = _("course") - verbose_name_plural = _("courses") + is_active = models.BooleanField(default=True,) def __str__(self): if self.code: @@ -469,185 +295,90 @@ def __str__(self): else: return f"{self.title}" - def save(self, *args, **kwargs): - self.slug = slugify(self.title) - super(Course, self).save(*args, **kwargs) - class Location(CommonInfo): - name = models.CharField( - max_length=255, - ) + name = models.CharField(max_length=255,) - address = models.CharField( - blank=True, - null=True, - max_length=255, - ) + address = models.CharField(blank=True, null=True, max_length=255,) - city = models.CharField( - blank=True, - null=True, - max_length=255, - ) + city = models.CharField(blank=True, null=True, max_length=255,) - state = models.CharField( - blank=True, - null=True, - max_length=2, - ) + state = models.CharField(blank=True, null=True, max_length=2,) - zip = models.CharField( - blank=True, - null=True, - max_length=20, - ) + zip = models.CharField(blank=True, null=True, max_length=20,) - is_active = models.BooleanField( - default=True, - ) + is_active = models.BooleanField(default=True,) class Meta: - ordering = ['-id'] + ordering = ["-id"] def __str__(self): return self.name class Session(CommonInfo): - MALE = 'male' - FEMALE = 'female' + MALE = "male" + FEMALE = "female" GENDER_LIMITATION_CHOICES = ( - (MALE, 'Male'), - (FEMALE, 'Female'), + (MALE, "Male"), + (FEMALE, "Female"), ) - course = models.ForeignKey( - Course, - on_delete=models.CASCADE, - limit_choices_to={'is_active': True}, - ) + course = models.ForeignKey(Course, on_delete=models.CASCADE, limit_choices_to={"is_active": True},) start_date = models.DateTimeField() - location = models.ForeignKey( - Location, - on_delete=models.CASCADE, - limit_choices_to={'is_active': True}, - ) + location = models.ForeignKey(Location, on_delete=models.CASCADE, limit_choices_to={"is_active": True},) - capacity = models.IntegerField( - default=20, - ) + capacity = models.IntegerField(default=20,) - mentor_capacity = models.IntegerField( - blank=True, - null=True, - ) + mentor_capacity = models.IntegerField(blank=True, null=True,) instructor = models.ForeignKey( Mentor, on_delete=models.CASCADE, related_name="session_instructor", - limit_choices_to={'user__groups__name': "Instructor"}, + limit_choices_to={"user__groups__name": "Instructor"}, ) # Pricing - cost = models.DecimalField( - max_digits=6, - decimal_places=2, - blank=True, - null=True, - ) + cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) - minimum_cost = models.DecimalField( - max_digits=6, - decimal_places=2, - blank=True, - null=True, - ) + minimum_cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) - maximum_cost = models.DecimalField( - max_digits=6, - decimal_places=2, - blank=True, - null=True, - ) + maximum_cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) - additional_info = models.TextField( - blank=True, - null=True, - help_text="Basic HTML allowed" - ) + additional_info = models.TextField(blank=True, null=True, help_text="Basic HTML allowed") - waitlist_mentors = models.ManyToManyField( - Mentor, - blank=True, - related_name="session_waitlist_mentors", - ) + waitlist_mentors = models.ManyToManyField(Mentor, blank=True, related_name="session_waitlist_mentors",) - waitlist_students = models.ManyToManyField( - Student, - blank=True, - related_name="session_waitlist_students", - ) + waitlist_students = models.ManyToManyField(Student, blank=True, related_name="session_waitlist_students",) external_enrollment_url = models.CharField( - max_length=255, - blank=True, - null=True, - help_text="When provided, local enrollment is disabled.", + max_length=255, blank=True, null=True, help_text="When provided, local enrollment is disabled.", ) - is_active = models.BooleanField( - default=False, - help_text="Session is active.", - ) + is_active = models.BooleanField(default=False, help_text="Session is active.",) - is_public = models.BooleanField( - default=False, - help_text="Session is a public session.", - ) + is_public = models.BooleanField(default=False, help_text="Session is a public session.",) - password = models.CharField( - blank=True, - max_length=255, - ) + password = models.CharField(blank=True, max_length=255,) - partner_message = models.TextField( - blank=True, - ) + partner_message = models.TextField(blank=True,) - announced_date_mentors = models.DateTimeField( - blank=True, - null=True, - ) + announced_date_mentors = models.DateTimeField(blank=True, null=True,) - announced_date_guardians = models.DateTimeField( - blank=True, - null=True, - ) + announced_date_guardians = models.DateTimeField(blank=True, null=True,) - image_url = models.CharField( - max_length=255, - blank=True, - null=True, - ) + image_url = models.CharField(max_length=255, blank=True, null=True,) - bg_image = models.ImageField( - blank=True, - null=True, - ) + bg_image = models.ImageField(blank=True, null=True,) - mentors_week_reminder_sent = models.BooleanField( - default=False, - ) + mentors_week_reminder_sent = models.BooleanField(default=False,) - mentors_day_reminder_sent = models.BooleanField( - default=False, - ) + mentors_day_reminder_sent = models.BooleanField(default=False,) gender_limitation = models.CharField( help_text="Limits the class to be only one gender.", @@ -662,10 +393,7 @@ class Session(CommonInfo): help_text="Only update this if different from the default.", blank=True, null=True, - validators=[ - MinValueValidator(0), - MaxValueValidator(100) - ], + validators=[MinValueValidator(0), MaxValueValidator(100)], ) override_maximum_age_limitation = models.IntegerField( @@ -673,25 +401,15 @@ class Session(CommonInfo): help_text="Only update this if different from the default.", blank=True, null=True, - validators=[ - MinValueValidator(0), - MaxValueValidator(100) - ], + validators=[MinValueValidator(0), MaxValueValidator(100)], ) online_video_link = models.URLField( - "Online Video Link", - help_text="Zoom link with password.", - blank=True, - null=True, + "Online Video Link", help_text="Zoom link with password.", blank=True, null=True, ) online_video_meeting_id = models.CharField( - "Online Video Meeting ID", - help_text="XXX XXXX XXXX", - max_length=255, - blank=True, - null=True, + "Online Video Meeting ID", help_text="XXX XXXX XXXX", max_length=255, blank=True, null=True, ) online_video_meeting_password = models.CharField( @@ -710,25 +428,12 @@ class Session(CommonInfo): ) # kept for older records - old_end_date = models.DateTimeField( - blank=True, - null=True, - ) - old_mentor_start_date = models.DateTimeField( - blank=True, - null=True, - ) - old_mentor_end_date = models.DateTimeField( - blank=True, - null=True, - ) - - class Meta: - verbose_name = _("session") - verbose_name_plural = _("sessions") + old_end_date = models.DateTimeField(blank=True, null=True,) + old_mentor_start_date = models.DateTimeField(blank=True, null=True,) + old_mentor_end_date = models.DateTimeField(blank=True, null=True,) def __str__(self): - date = formats.date_format(self.start_date, 'SHORT_DATETIME_FORMAT') + date = formats.date_format(self.start_date, "SHORT_DATETIME_FORMAT") return f"{self.course.title} | {date}" def save(self, *args, **kwargs): @@ -738,77 +443,55 @@ def save(self, *args, **kwargs): super(Session, self).save(*args, **kwargs) def get_absolute_url(self): - return reverse('session-detail', args=[str(self.id)]) + return reverse("session-detail", args=[str(self.id)]) def get_sign_up_url(self): - return reverse('session-sign-up', args=[str(self.id)]) + return reverse("session-sign-up", args=[str(self.id)]) def get_calendar_url(self): - return reverse('session-calendar', args=[str(self.id)]) + return reverse("session-calendar", args=[str(self.id)]) def get_current_orders(self, checked_in=None): if checked_in is not None: if checked_in: - orders = Order.objects.filter( - is_active=True, - session=self - ).exclude(check_in=None).order_by('student__last_name') + orders = ( + Order.objects.filter(is_active=True, session=self) + .exclude(check_in=None) + .order_by("student__last_name") + ) else: - orders = Order.objects.filter( - is_active=True, - session=self, - check_in=None - ).order_by('student__last_name') + orders = Order.objects.filter(is_active=True, session=self, check_in=None).order_by( + "student__last_name" + ) else: - orders = Order.objects.filter( - is_active=True, - session=self - ).order_by('check_in', 'student__last_name') + orders = Order.objects.filter(is_active=True, session=self).order_by("check_in", "student__last_name") return orders def get_mentor_orders(self): - orders = MentorOrder.objects.filter( - session=self, - is_active=True, - ).order_by('mentor__user__last_name') + orders = MentorOrder.objects.filter(session=self, is_active=True,).order_by("mentor__user__last_name") return orders def get_checked_in_mentor_orders(self): - orders = MentorOrder.objects.filter( - session=self, - is_active=True, - check_in__isnull=False - ).order_by('mentor__user__last_name') + orders = MentorOrder.objects.filter(session=self, is_active=True, check_in__isnull=False).order_by( + "mentor__user__last_name" + ) return orders def get_current_students(self, checked_in=None): if checked_in is not None: if checked_in: - orders = Order.objects.filter( - is_active=True, - session=self - ).exclude(check_in=None).values('student') + orders = Order.objects.filter(is_active=True, session=self).exclude(check_in=None).values("student") else: - orders = Order.objects.filter( - is_active=True, - session=self, - check_in=None - ).values('student') + orders = Order.objects.filter(is_active=True, session=self, check_in=None).values("student") else: - orders = Order.objects.filter( - is_active=True, - session=self - ).values('student') + orders = Order.objects.filter(is_active=True, session=self).values("student") return orders def get_checked_in_students(self): - return Order.objects.filter( - is_active=True, - session=self - ).exclude(check_in=None).values('student') + return Order.objects.filter(is_active=True, session=self).exclude(check_in=None).values("student") def get_mentor_capacity(self): if self.mentor_capacity: @@ -818,9 +501,10 @@ def get_mentor_capacity(self): def is_guardian_announced(self): return self.announced_date_guardians is not None + is_guardian_announced.boolean = True is_guardian_announced.short_description = "Is Announced" - is_guardian_announced.admin_order_field = 'announced_date_guardians' + is_guardian_announced.admin_order_field = "announced_date_guardians" @property def end_date(self): @@ -869,292 +553,152 @@ def maximum_age(self): class MeetingType(CommonInfo): - code = models.CharField( - max_length=255, - blank=True, - null=True, - ) - - title = models.CharField( - max_length=255, - ) + code = models.CharField(max_length=255, blank=True, null=True,) - slug = models.SlugField( - max_length=40, - blank=True, - null=True, - ) + title = models.CharField(max_length=255,) - description = models.TextField( - blank=True, - null=True, - help_text="Basic HTML allowed", - ) + slug = models.SlugField(max_length=40, blank=True, null=True,) - class Meta: - verbose_name = _("meeting type") - verbose_name_plural = _("meeting types") + description = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) def __str__(self): if self.code: - return f'{self.code} | {self.title}' + return f"{self.code} | {self.title}" else: - return f'{self.title}' - - def save(self, *args, **kwargs): - self.slug = slugify(self.title) - super(MeetingType, self).save(*args, **kwargs) + return f"{self.title}" class Meeting(CommonInfo): - meeting_type = models.ForeignKey( - MeetingType, - on_delete=models.CASCADE, - ) + meeting_type = models.ForeignKey(MeetingType, on_delete=models.CASCADE,) - additional_info = models.TextField( - blank=True, - null=True, - help_text="Basic HTML allowed", - ) + additional_info = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - start_date = models.DateTimeField( - blank=True, - null=True, - ) + start_date = models.DateTimeField(blank=True, null=True,) - end_date = models.DateTimeField( - blank=True, - null=True, - ) + end_date = models.DateTimeField(blank=True, null=True,) - location = models.ForeignKey( - Location, - on_delete=models.CASCADE, - ) + location = models.ForeignKey(Location, on_delete=models.CASCADE,) external_enrollment_url = models.CharField( - max_length=255, - blank=True, - null=True, - help_text="When provided, local enrollment is disabled.", + max_length=255, blank=True, null=True, help_text="When provided, local enrollment is disabled.", ) - is_public = models.BooleanField( - default=False, - ) + is_public = models.BooleanField(default=False,) - is_active = models.BooleanField( - default=False, - ) + is_active = models.BooleanField(default=False,) - image_url = models.CharField( - max_length=255, - blank=True, - null=True, - ) - - bg_image = models.ImageField( - blank=True, - null=True, - ) + image_url = models.CharField(max_length=255, blank=True, null=True,) - announced_date = models.DateTimeField( - blank=True, - null=True, - ) + bg_image = models.ImageField(blank=True, null=True,) - class Meta: - verbose_name = _("meeting") - verbose_name_plural = _("meetings") + announced_date = models.DateTimeField(blank=True, null=True,) def __str__(self): date = formats.date_format(self.start_date, "SHORT_DATETIME_FORMAT") - return f'{self.meeting_type.title} | {date}' + return f"{self.meeting_type.title} | {date}" def get_absolute_url(self): - return reverse('meeting-detail', args=[str(self.id)]) + return reverse("meeting-detail", args=[str(self.id)]) def get_sign_up_url(self): - return reverse('meeting-register', args=[str(self.id)]) + return reverse("meeting-register", args=[str(self.id)]) def get_calendar_url(self): - return reverse('meeting-calendar', args=[str(self.id)]) + return reverse("meeting-calendar", args=[str(self.id)]) def get_current_orders(self, checked_in=None): if checked_in is not None: if checked_in: - orders = MeetingOrder.objects.filter( - is_active=True, - meeting=self, - ).exclude( - check_in=None, - ).order_by( - 'mentor__user__last_name' + orders = ( + MeetingOrder.objects.filter(is_active=True, meeting=self,) + .exclude(check_in=None,) + .order_by("mentor__user__last_name") ) else: - orders = MeetingOrder.objects.filter( - is_active=True, - meeting=self, - check_in=None, - ).order_by( - 'mentor__user__last_name' + orders = MeetingOrder.objects.filter(is_active=True, meeting=self, check_in=None,).order_by( + "mentor__user__last_name" ) else: - orders = MeetingOrder.objects.filter( - is_active=True, - meeting=self, - ).order_by( - 'check_in', - 'mentor__user__last_name', + orders = MeetingOrder.objects.filter(is_active=True, meeting=self,).order_by( + "check_in", "mentor__user__last_name", ) return orders def get_current_mentors(self): return Mentor.objects.filter( - id__in=MeetingOrder.objects.filter( - is_active=True, - meeting=self, - ).values( - 'mentor__id', - ) + id__in=MeetingOrder.objects.filter(is_active=True, meeting=self,).values("mentor__id",) ) def get_mentor_count(self): return MeetingOrder.objects.filter(meeting__id=self.id).count() - get_mentor_count.short_description = 'Mentors' + + get_mentor_count.short_description = "Mentors" class Order(CommonInfo): - guardian = models.ForeignKey( - Guardian, - on_delete=models.CASCADE, - ) + guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE,) - session = models.ForeignKey( - Session, - on_delete=models.CASCADE, - ) + session = models.ForeignKey(Session, on_delete=models.CASCADE,) - student = models.ForeignKey( - Student, - on_delete=models.CASCADE, - ) + student = models.ForeignKey(Student, on_delete=models.CASCADE,) - is_active = models.BooleanField( - default=True, - ) + is_active = models.BooleanField(default=True,) - ip = models.CharField( - max_length=255, - blank=True, - null=True, - ) + ip = models.CharField(max_length=255, blank=True, null=True,) - check_in = models.DateTimeField( - blank=True, - null=True, - ) - - alternate_guardian = models.CharField( - max_length=255, - blank=True, - null=True, - ) + check_in = models.DateTimeField(blank=True, null=True,) - affiliate = models.CharField( - max_length=255, - blank=True, - null=True, - ) + alternate_guardian = models.CharField(max_length=255, blank=True, null=True,) - order_number = models.CharField( - max_length=255, - blank=True, - null=True, - ) + affiliate = models.CharField(max_length=255, blank=True, null=True,) - week_reminder_sent = models.BooleanField( - default=False, - ) + order_number = models.CharField(max_length=255, blank=True, null=True,) - day_reminder_sent = models.BooleanField( - default=False, - ) + week_reminder_sent = models.BooleanField(default=False,) - class Meta: - verbose_name = _("order") - verbose_name_plural = _("orders") + day_reminder_sent = models.BooleanField(default=False,) def __str__(self): - return f'{self.student.first_name} {self.student.last_name} | {self.session.course.title}' + return f"{self.student.first_name} {self.student.last_name} | {self.session.course.title}" def is_checked_in(self): return self.check_in is not None + is_checked_in.boolean = True def get_student_age(self): return self.student.get_age(self.session.start_date) - get_student_age.short_description = 'Age' + + get_student_age.short_description = "Age" def get_student_gender(self): return self.student.get_clean_gender().title() - get_student_gender.short_description = 'Gender' + get_student_gender.short_description = "Gender" -class MentorOrder(CommonInfo): - mentor = models.ForeignKey( - Mentor, - on_delete=models.CASCADE, - ) - session = models.ForeignKey( - Session, - on_delete=models.CASCADE, - ) +class MentorOrder(CommonInfo): + mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE,) - is_active = models.BooleanField( - default=True, - ) + session = models.ForeignKey(Session, on_delete=models.CASCADE,) - ip = models.CharField( - max_length=255, - blank=True, - null=True, - ) + is_active = models.BooleanField(default=True,) - check_in = models.DateTimeField( - blank=True, - null=True, - ) + ip = models.CharField(max_length=255, blank=True, null=True,) - affiliate = models.CharField( - max_length=255, - blank=True, - null=True, - ) + check_in = models.DateTimeField(blank=True, null=True,) - order_number = models.CharField( - max_length=255, - blank=True, - null=True, - ) + affiliate = models.CharField(max_length=255, blank=True, null=True,) - week_reminder_sent = models.BooleanField( - default=False, - ) + order_number = models.CharField(max_length=255, blank=True, null=True,) - day_reminder_sent = models.BooleanField( - default=False, - ) + week_reminder_sent = models.BooleanField(default=False,) - class Meta: - verbose_name = _("mentor order") - verbose_name_plural = _("mentor orders") + day_reminder_sent = models.BooleanField(default=False,) def __str__(self): return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.session.course.title}" @@ -1175,144 +719,76 @@ def save(self, *args, **kwargs): class MeetingOrder(CommonInfo): - mentor = models.ForeignKey( - Mentor, - on_delete=models.CASCADE, - ) + mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE,) - meeting = models.ForeignKey( - Meeting, - on_delete=models.CASCADE, - ) + meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE,) - is_active = models.BooleanField( - default=True, - ) + is_active = models.BooleanField(default=True,) - ip = models.CharField( - max_length=255, - blank=True, - null=True, - ) + ip = models.CharField(max_length=255, blank=True, null=True,) - check_in = models.DateTimeField( - blank=True, - null=True, - ) + check_in = models.DateTimeField(blank=True, null=True,) - affiliate = models.CharField( - max_length=255, - blank=True, - null=True, - ) + affiliate = models.CharField(max_length=255, blank=True, null=True,) - order_number = models.CharField( - max_length=255, - blank=True, - null=True, - ) + order_number = models.CharField(max_length=255, blank=True, null=True,) - week_reminder_sent = models.BooleanField( - default=False, - ) + week_reminder_sent = models.BooleanField(default=False,) - day_reminder_sent = models.BooleanField( - default=False, - ) - - class Meta: - verbose_name = _("meeting order") - verbose_name_plural = _("meeting orders") + day_reminder_sent = models.BooleanField(default=False,) def __str__(self): return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.meeting.meeting_type.title}" def is_checked_in(self): return self.check_in is not None + is_checked_in.boolean = True class EquipmentType(CommonInfo): - name = models.CharField( - max_length=255, - blank=False, - null=False, - ) + name = models.CharField(max_length=255, blank=False, null=False,) def __str__(self): return self.name class Equipment(CommonInfo): - WORKING = 'working' - ISSUE = 'issue' - UNUSABLE = 'unusable' + WORKING = "working" + ISSUE = "issue" + UNUSABLE = "unusable" EQUIPMENT_CONDITIONS = [ - (WORKING, 'Working'), - (ISSUE, 'Issue'), - (UNUSABLE, 'Unusable'), + (WORKING, "Working"), + (ISSUE, "Issue"), + (UNUSABLE, "Unusable"), ] - uuid = models.CharField( - max_length=255, - verbose_name="UUID", - default='000-000-000-000', - null=False, - ) + uuid = models.CharField(max_length=255, verbose_name="UUID", default="000-000-000-000", null=False,) - equipment_type = models.ForeignKey( - EquipmentType, - on_delete=models.CASCADE, - ) + equipment_type = models.ForeignKey(EquipmentType, on_delete=models.CASCADE,) - make = models.CharField( - max_length=255, - ) + make = models.CharField(max_length=255,) - model = models.CharField( - max_length=255, - ) + model = models.CharField(max_length=255,) - asset_tag = models.CharField( - max_length=255, - ) + asset_tag = models.CharField(max_length=255,) - acquisition_date = models.DateTimeField( - blank=True, - null=True, - ) + acquisition_date = models.DateTimeField(blank=True, null=True,) - condition = models.CharField( - max_length=255, - choices=EQUIPMENT_CONDITIONS, - ) + condition = models.CharField(max_length=255, choices=EQUIPMENT_CONDITIONS,) - notes = models.TextField( - blank=True, - null=True, - ) + notes = models.TextField(blank=True, null=True,) - last_system_update_check_in = models.DateTimeField( - blank=True, - null=True, - verbose_name="Last Check In", - ) + last_system_update_check_in = models.DateTimeField(blank=True, null=True, verbose_name="Last Check In",) - last_system_update = models.DateTimeField( - blank=True, - null=True, - verbose_name="Last Update", - ) + last_system_update = models.DateTimeField(blank=True, null=True, verbose_name="Last Update",) - force_update_on_next_boot = models.BooleanField( - default=False, - verbose_name="Force Update", - ) + force_update_on_next_boot = models.BooleanField(default=False, verbose_name="Force Update",) class Meta: - verbose_name = _("equipment") - verbose_name_plural = _("equipment") + verbose_name = "equipment" + verbose_name_plural = "equipment" def __str__(self): return f"{self.equipment_type.name} | {self.make} {self.model} | {self.acquisition_date}" @@ -1320,27 +796,17 @@ def __str__(self): class EmailContent(CommonInfo): - nickname = models.CharField( - max_length=255, - ) + nickname = models.CharField(max_length=255,) - subject = models.CharField( - max_length=255, - ) + subject = models.CharField(max_length=255,) - body = models.TextField( - blank=True, - null=True, - help_text="Basic HTML allowed", - ) + body = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - is_active = models.BooleanField( - default=True, - ) + is_active = models.BooleanField(default=True,) class Meta: - verbose_name = _("email content") - verbose_name_plural = _("email content") + verbose_name = "email content" + verbose_name_plural = "email content" def __str__(self): return f"{self.nickname} | {self.subject}" @@ -1348,101 +814,61 @@ def __str__(self): class Donation(CommonInfo): - user = models.ForeignKey( - CDCUser, - blank=True, - null=True, - on_delete=models.CASCADE, - ) + user = models.ForeignKey(CDCUser, blank=True, null=True, on_delete=models.CASCADE,) - session = models.ForeignKey( - Session, - blank=True, - null=True, - on_delete=models.CASCADE, - ) + session = models.ForeignKey(Session, blank=True, null=True, on_delete=models.CASCADE,) - first_name = models.CharField( - max_length=255, - blank=True, - null=True, - ) + first_name = models.CharField(max_length=255, blank=True, null=True,) - last_name = models.CharField( - max_length=255, - blank=True, - null=True, - ) + last_name = models.CharField(max_length=255, blank=True, null=True,) - referral_code = models.CharField( - max_length=255, - blank=True, - null=True, - ) + referral_code = models.CharField(max_length=255, blank=True, null=True,) - email = models.EmailField( - blank=True, - null=True, - ) + email = models.EmailField(blank=True, null=True,) amount = models.IntegerField() - is_verified = models.BooleanField( - default=False, - ) - - receipt_sent = models.BooleanField( - default=False, - ) + is_verified = models.BooleanField(default=False,) - class Meta: - verbose_name = _("donation") - verbose_name_plural = _("donations") + receipt_sent = models.BooleanField(default=False,) def __str__(self): return f"{self.email} | ${self.amount}" def get_admin_url(self): content_type = ContentType.objects.get_for_model(self.__class__) - return reverse( - f"admin:{content_type.app_label}_{content_type.model}_change", - args=(self.id,) - ) + return reverse(f"admin:{content_type.app_label}_{content_type.model}_change", args=(self.id,),) def get_first_name(self): if self.user: return self.user.first_name else: return self.first_name - get_first_name.short_description = 'First Name' + + get_first_name.short_description = "First Name" def get_last_name(self): if self.user: return self.user.last_name else: return self.last_name - get_last_name.short_description = 'Last Name' + + get_last_name.short_description = "Last Name" def get_email(self): if self.user: return self.user.email else: return self.email - get_email.short_description = 'Email' + + get_email.short_description = "Email" class PartnerPasswordAccess(CommonInfo): - user = models.ForeignKey( - CDCUser, - on_delete=models.CASCADE, - ) + user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - session = models.ForeignKey( - Session, - on_delete=models.CASCADE, - ) + session = models.ForeignKey(Session, on_delete=models.CASCADE,) class Meta: - verbose_name = _("partner_password_access") - db_table = _("partner_password_access") + db_table = "partner_password_access" diff --git a/coderdojochi/notifications.py b/coderdojochi/notifications.py index 28b2064c..bd06442e 100644 --- a/coderdojochi/notifications.py +++ b/coderdojochi/notifications.py @@ -1,60 +1,38 @@ import logging -import requests from django.conf import settings +import requests + logger = logging.getLogger(__name__) class SlackNotification: - DEFAULT_PAYLOAD = { - "channel": settings.SLACK_ALERTS_CHANNEL, - "text": "IMPLEMENT_ME" - } + DEFAULT_PAYLOAD = {"channel": settings.SLACK_ALERTS_CHANNEL, "text": "IMPLEMENT_ME"} def __init__(self): self.payload = DEFAULT_PAYLOAD def send(self): - res = requests.post(settings.SLACK_WEBHOOK_URL, json={ - **self.DEFAULT_PAYLOAD, - **self.payload - }) + res = requests.post(settings.SLACK_WEBHOOK_URL, json={**self.DEFAULT_PAYLOAD, **self.payload}) if res.status_code != requests.codes.ok: - logger.error({ - "msg": "Unable to send Slack notification", - "error": res.content - }) + logger.error({"msg": "Unable to send Slack notification", "error": res.content}) class NewMentorNotification(SlackNotification): def __init__(self, mentor): self.payload = { "blocks": [ - { - "type": "divider" - }, - { - "type": "section", - "text": { - "text": "👋 New mentor signup!", - "type": "mrkdwn" - } - }, + {"type": "divider"}, + {"type": "section", "text": {"text": "👋 New mentor signup!", "type": "mrkdwn"}}, { "type": "section", "fields": [ - { - "type": "mrkdwn", - "text": f"*Name*: \n{mentor.user.name}" - }, - { - "type": "mrkdwn", - "text": f"*Email*: \n{mentor.user.email}" - } - ] - } + {"type": "mrkdwn", "text": f"*Name*: \n{mentor.user.name}"}, + {"type": "mrkdwn", "text": f"*Email*: \n{mentor.user.email}"}, + ], + }, ] } @@ -68,37 +46,17 @@ def __init__(self, mentor_order): self.payload = { "blocks": [ - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "🏫 New mentor enrollment!" - } - }, + {"type": "divider"}, + {"type": "section", "text": {"type": "mrkdwn", "text": "🏫 New mentor enrollment!"}}, { "type": "section", "fields": [ - { - "type": "mrkdwn", - "text": f"*Name*: \n{name}\n" - }, - { - "type": "mrkdwn", - "text": f"*Email*: \n{email}\n" - }, - { - "type": "mrkdwn", - "text": f"*Location*: \n{location}" - }, - { - "type": "mrkdwn", - "text": f"*Date*: \n{start_date}" - } - ] - } + {"type": "mrkdwn", "text": f"*Name*: \n{name}\n"}, + {"type": "mrkdwn", "text": f"*Email*: \n{email}\n"}, + {"type": "mrkdwn", "text": f"*Location*: \n{location}"}, + {"type": "mrkdwn", "text": f"*Date*: \n{start_date}"}, + ], + }, ] } @@ -107,28 +65,14 @@ class NewMentorBgCheckNotification(SlackNotification): def __init__(self, mentor): self.payload = { "blocks": [ - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "✅ New mentor background check!" - } - }, + {"type": "divider"}, + {"type": "section", "text": {"type": "mrkdwn", "text": "✅ New mentor background check!"}}, { "type": "section", "fields": [ - { - "type": "mrkdwn", - "text": f"*Name*: \n{mentor.user.name}" - }, - { - "type": "mrkdwn", - "text": f"*Email*: \n{mentor.user.email}" - } - ] - } + {"type": "mrkdwn", "text": f"*Name*: \n{mentor.user.name}"}, + {"type": "mrkdwn", "text": f"*Email*: \n{mentor.user.email}"}, + ], + }, ] } diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 3dc34db5..6a16653b 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -1,8 +1,7 @@ -import calendar import logging import operator from collections import Counter -from datetime import date, timedelta +from datetime import timedelta from functools import reduce from django.conf import settings @@ -10,29 +9,17 @@ from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required from django.core.exceptions import ObjectDoesNotExist -from django.core.mail import EmailMultiAlternatives, get_connection from django.db.models import Case, Count, IntegerField, When from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone -from django.utils.html import strip_tags from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_exempt -from django.views.generic import TemplateView import arrow -from dateutil.relativedelta import relativedelta -from icalendar import Calendar, Event, vText - -from coderdojochi.forms import ( - CDCModelForm, - ContactForm, - DonationForm, - GuardianForm, - MentorForm, - StudentForm, -) + +from coderdojochi.forms import DonationForm, StudentForm from coderdojochi.models import ( Donation, Equipment, @@ -43,7 +30,6 @@ Mentor, MentorOrder, Order, - PartnerPasswordAccess, Session, Student, ) @@ -56,62 +42,42 @@ def home(request, template_name="home.html"): - upcoming_classes = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now(), - ).order_by('start_date') - - if ( - not request.user.is_authenticated or - not request.user.role == 'mentor' - ): + upcoming_classes = Session.objects.filter(is_active=True, start_date__gte=timezone.now(),).order_by("start_date") + + if not request.user.is_authenticated or not request.user.role == "mentor": upcoming_classes = upcoming_classes.filter(is_public=True) upcoming_classes = upcoming_classes[:3] - return render(request, template_name, { - 'upcoming_classes': upcoming_classes - }) + return render(request, template_name, {"upcoming_classes": upcoming_classes}) def volunteer(request, template_name="volunteer.html"): - mentors = Mentor.objects.select_related('user').filter( - is_active=True, - is_public=True, - background_check=True, - avatar_approved=True, - ).annotate( - session_count=Count('mentororder') - ).order_by('-user__role', '-session_count') - - upcoming_meetings = Meeting.objects.filter( - is_active=True, - is_public=True, - end_date__gte=timezone.now() - ).order_by('start_date')[:3] - - return render(request, template_name, { - 'mentors': mentors, - 'upcoming_meetings': upcoming_meetings - }) + mentors = ( + Mentor.objects.select_related("user") + .filter(is_active=True, is_public=True, background_check=True, avatar_approved=True,) + .annotate(session_count=Count("mentororder")) + .order_by("-user__role", "-session_count") + ) + + upcoming_meetings = Meeting.objects.filter(is_active=True, is_public=True, end_date__gte=timezone.now()).order_by( + "start_date" + )[:3] + + return render(request, template_name, {"mentors": mentors, "upcoming_meetings": upcoming_meetings}) def mentors(request, template_name="mentors.html"): mentors = Mentor.objects.filter( - is_active=True, - is_public=True, - background_check=True, - avatar_approved=True, - ).order_by('user__date_joined') + is_active=True, is_public=True, background_check=True, avatar_approved=True, + ).order_by("user__date_joined") # mentors = Mentor.objects.filter( # is_active=True, # is_public=True # ).order_by('user__date_joined') - return render(request, template_name, { - 'mentors': mentors - }) + return render(request, template_name, {"mentors": mentors}) def mentor_detail(request, pk=None, template_name="mentor-detail.html"): @@ -119,20 +85,11 @@ def mentor_detail(request, pk=None, template_name="mentor-detail.html"): mentor = get_object_or_404(Mentor, id=pk) if not mentor.is_public: - messages.error( - request, - 'Invalid mentor ID.' - ) + messages.error(request, "Invalid mentor ID.") - return redirect('mentors') + return redirect("mentors") - return render( - request, - template_name, - { - 'mentor': mentor - } - ) + return render(request, template_name, {"mentor": mentor}) @login_required @@ -140,14 +97,9 @@ def mentor_approve_avatar(request, pk=None): mentor = get_object_or_404(Mentor, id=pk) if not request.user.is_staff: - messages.error( - request, - 'You do not have permissions to moderate content.' - ) + messages.error(request, "You do not have permissions to moderate content.") - return redirect( - f"{reverse('account_login')}?next={mentor.get_approve_avatar_url()}" - ) + return redirect(f"{reverse('account_login')}?next={mentor.get_approve_avatar_url()}") mentor.avatar_approved = True mentor.save() @@ -155,12 +107,10 @@ def mentor_approve_avatar(request, pk=None): if mentor.background_check: messages.success( request, - f"{mentor.user.first_name} {mentor.user.last_name}'s avatar approved and their account is now public." + f"{mentor.user.first_name} {mentor.user.last_name}'s avatar approved and their account is now public.", ) - return redirect( - f"{reverse('mentors')}{mentor.id}" - ) + return redirect(f"{reverse('mentors')}{mentor.id}") else: messages.success( @@ -168,10 +118,10 @@ def mentor_approve_avatar(request, pk=None): ( f"{mentor.user.first_name}{mentor.user.last_name}'s avatar approved but they have yet " f"to fill out the 'background search' form." - ) + ), ) - return redirect('mentors') + return redirect("mentors") @login_required @@ -179,24 +129,17 @@ def mentor_reject_avatar(request, pk=None): mentor = get_object_or_404(Mentor, id=pk) if not request.user.is_staff: - messages.error( - request, - 'You do not have permissions to moderate content.' - ) + messages.error(request, "You do not have permissions to moderate content.") - return redirect( - f"{reverse('account_login')}?next={mentor.get_reject_avatar_url()}" - ) + return redirect(f"{reverse('account_login')}?next={mentor.get_reject_avatar_url()}") mentor.avatar_approved = False mentor.save() email( - subject='Your We All Code avatar...', - template_name='class-announcement-mentor', - merge_global_data={ - 'site_url': settings.SITE_URL, - }, + subject="Your We All Code avatar...", + template_name="class-announcement-mentor", + merge_global_data={"site_url": settings.SITE_URL,}, recipients=[mentor.user.email], ) @@ -205,31 +148,27 @@ def mentor_reject_avatar(request, pk=None): ( f"{mentor.user.first_name} {mentor.user.last_name}'s avatar rejected and their account " f"is no longer public. An email notice has been sent to the mentor." - ) + ), ) - return redirect('mentors') + return redirect("mentors") @login_required -def student_detail( - request, - student_id=False, - template_name="student-detail.html" -): +def student_detail(request, student_id=False, template_name="student-detail.html"): access = True - if request.user.role == 'guardian' and student_id: + if request.user.role == "guardian" and student_id: # for the specific student redirect to admin page try: student = Student.objects.get(id=student_id, is_active=True) except ObjectDoesNotExist: - return redirect('account_home') + return redirect("account_home") try: guardian = Guardian.objects.get(user=request.user, is_active=True) except ObjectDoesNotExist: - return redirect('account_home') + return redirect("account_home") if not student.guardian == guardian: access = False @@ -239,161 +178,88 @@ def student_detail( access = False if not access: - return redirect('account_home') - messages.error( - request, - 'You do not have permissions to edit this student.' - ) + return redirect("account_home") + messages.error(request, "You do not have permissions to edit this student.") - if request.method == 'POST': - if 'delete' in request.POST: + if request.method == "POST": + if "delete" in request.POST: student.is_active = False student.save() - messages.success( - request, - f"Student \"{student.first_name} {student.last_name}\" Deleted." - ) - return redirect('account_home') + messages.success(request, f'Student "{student.first_name} {student.last_name}" Deleted.') + return redirect("account_home") form = StudentForm(request.POST, instance=student) if form.is_valid(): form.save() - messages.success(request, 'Student Updated.') - return redirect('account_home') + messages.success(request, "Student Updated.") + return redirect("account_home") - return render( - request, - template_name, - { - 'form': form - } - ) + return render(request, template_name, {"form": form}) @login_required def cdc_admin(request, template_name="admin.html"): if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) - return redirect('weallcode-home') - - sessions = Session.objects.select_related().annotate( - num_orders=Count( - 'order' - ), - - num_attended=Count( - Case( - When( - order__check_in__isnull=False, - then=1 - ) - ) - ), + messages.error(request, "You do not have permission to access this page.") + return redirect("weallcode-home") - is_future=Case( - When( - start_date__gte=timezone.now(), - then=1 - ), - default=0, - output_field=IntegerField(), + sessions = ( + Session.objects.select_related() + .annotate( + num_orders=Count("order"), + num_attended=Count(Case(When(order__check_in__isnull=False, then=1))), + is_future=Case(When(start_date__gte=timezone.now(), then=1), default=0, output_field=IntegerField(),), ) - ).order_by( - '-start_date' + .order_by("-start_date") ) - meetings = Meeting.objects.select_related().annotate( - num_orders=Count( - 'meetingorder' - ), - - num_attended=Count( - Case( - When( - meetingorder__check_in__isnull=False, - then=1 - ) - ) - ), - - is_future=Case( - When( - end_date__gte=timezone.now(), - then=1 - ), - default=0, - output_field=IntegerField(), + meetings = ( + Meeting.objects.select_related() + .annotate( + num_orders=Count("meetingorder"), + num_attended=Count(Case(When(meetingorder__check_in__isnull=False, then=1))), + is_future=Case(When(end_date__gte=timezone.now(), then=1), default=0, output_field=IntegerField(),), ) - - ).order_by( - '-start_date' + .order_by("-start_date") ) orders = Order.objects.select_related() total_past_orders = orders.filter(is_active=True) total_past_orders_count = total_past_orders.count() - total_checked_in_orders = orders.filter( - is_active=True, - check_in__isnull=False - ) + total_checked_in_orders = orders.filter(is_active=True, check_in__isnull=False) total_checked_in_orders_count = total_checked_in_orders.count() # Genders - gender_count = list( - Counter( - e.student.get_clean_gender() for e in total_checked_in_orders - ).items() - ) - gender_count = sorted( - list(dict(gender_count).items()), - key=operator.itemgetter(1) - ) + gender_count = list(Counter(e.student.get_clean_gender() for e in total_checked_in_orders).items()) + gender_count = sorted(list(dict(gender_count).items()), key=operator.itemgetter(1)) # Ages - ages = sorted( - list( - e.student.get_age(e.session.start_date) for e in total_checked_in_orders - ) - ) - age_count = sorted( - list(dict( - list( - Counter(ages).items() - ) - ).items()), - key=operator.itemgetter(0) - ) + ages = sorted(list(e.student.get_age(e.session.start_date) for e in total_checked_in_orders)) + age_count = sorted(list(dict(list(Counter(ages).items())).items()), key=operator.itemgetter(0)) # Average Age - average_age = int( - round( - sum(ages) / float(len(ages)) - ) - ) + average_age = int(round(sum(ages) / float(len(ages)))) return render( request, template_name, { - 'age_count': age_count, - 'average_age': average_age, - 'gender_count': gender_count, - 'meetings': meetings, + "age_count": age_count, + "average_age": average_age, + "gender_count": gender_count, + "meetings": meetings, # 'past_meetings_count': past_meetings_count, # 'past_sessions': past_sessions, # 'past_sessions_count': past_sessions_count, - 'sessions': sessions, - 'total_checked_in_orders_count': total_checked_in_orders_count, - 'total_past_orders_count': total_past_orders_count, + "sessions": sessions, + "total_checked_in_orders_count": total_checked_in_orders_count, + "total_past_orders_count": total_past_orders_count, # 'upcoming_meetings': upcoming_meetings, # 'upcoming_meetings_count': upcoming_meetings_count, # 'upcoming_sessions': upcoming_sessions, # 'upcoming_sessions_count': upcoming_sessions_count, - } + }, ) @@ -402,59 +268,32 @@ def cdc_admin(request, template_name="admin.html"): def session_stats(request, pk, template_name="session-stats.html"): if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) - return redirect('weallcode-home') + messages.error(request, "You do not have permission to access this page.") + return redirect("weallcode-home") session_obj = get_object_or_404(Session, pk=pk) - current_orders_checked_in = session_obj.get_current_orders( - checked_in=True - ) + current_orders_checked_in = session_obj.get_current_orders(checked_in=True) - students_checked_in = current_orders_checked_in.values('student') + students_checked_in = current_orders_checked_in.values("student") if students_checked_in: attendance_percentage = round( - ( - float(current_orders_checked_in.count()) / - float(session_obj.get_current_students().count()) - ) * 100 + (float(current_orders_checked_in.count()) / float(session_obj.get_current_students().count())) * 100 ) else: attendance_percentage = False # Genders - gender_count = list( - Counter( - e.student.get_clean_gender() - for e in session_obj.get_current_orders() - ).items() - ) + gender_count = list(Counter(e.student.get_clean_gender() for e in session_obj.get_current_orders()).items()) - gender_count = sorted( - list(dict(gender_count).items()), - key=operator.itemgetter(1) - ) + gender_count = sorted(list(dict(gender_count).items()), key=operator.itemgetter(1)) # Ages - ages = sorted( - list( - e.student.get_age(e.session.start_date) for e in session_obj.get_current_orders() - ) - ) + ages = sorted(list(e.student.get_age(e.session.start_date) for e in session_obj.get_current_orders())) - age_count = sorted( - list(dict( - list( - Counter(ages).items() - ) - ).items()), - key=operator.itemgetter(1) - ) + age_count = sorted(list(dict(list(Counter(ages).items())).items()), key=operator.itemgetter(1)) # Average Age average_age = False @@ -463,25 +302,19 @@ def session_stats(request, pk, template_name="session-stats.html"): for order in current_orders_checked_in: student_ages.append(order.student.get_age(order.session.start_date)) - average_age = ( - reduce( - lambda x, y: x + y, - student_ages - ) / - len(student_ages) - ) + average_age = reduce(lambda x, y: x + y, student_ages) / len(student_ages) return render( request, template_name, { - 'session': session_obj, - 'students_checked_in': students_checked_in, - 'attendance_percentage': attendance_percentage, - 'average_age': average_age, - 'age_count': age_count, - 'gender_count': gender_count - } + "session": session_obj, + "students_checked_in": students_checked_in, + "attendance_percentage": attendance_percentage, + "average_age": average_age, + "age_count": age_count, + "gender_count": gender_count, + }, ) @@ -490,19 +323,13 @@ def session_stats(request, pk, template_name="session-stats.html"): def session_check_in(request, pk, template_name="session-check-in.html"): if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) + messages.error(request, "You do not have permission to access this page.") - return redirect('weallcode-home') + return redirect("weallcode-home") - if request.method == 'POST': - if 'order_id' in request.POST: - order = get_object_or_404( - Order, - id=request.POST['order_id'] - ) + if request.method == "POST": + if "order_id" in request.POST: + order = get_object_or_404(Order, id=request.POST["order_id"]) if order.check_in: order.check_in = None @@ -510,16 +337,14 @@ def session_check_in(request, pk, template_name="session-check-in.html"): order.check_in = timezone.now() if ( - f"{order.guardian.user.first_name} {order.guardian.user.last_name}" != - request.POST['order_alternate_guardian'] + f"{order.guardian.user.first_name} {order.guardian.user.last_name}" + != request.POST["order_alternate_guardian"] ): - order.alternate_guardian = request.POST[ - 'order_alternate_guardian' - ] + order.alternate_guardian = request.POST["order_alternate_guardian"] order.save() else: - messages.error(request, 'Invalid Order') + messages.error(request, "Invalid Order") # Get current session session = get_object_or_404(Session, pk=pk) @@ -528,81 +353,37 @@ def session_check_in(request, pk, template_name="session-check-in.html"): active_session = True if timezone.now() < session.end_date else False # get the orders - orders = Order.objects.select_related().filter(session_id=pk).annotate( - num_attended=Count( - Case( - When( - student__order__check_in__isnull=False, - then=1 - ) - ) - ), - num_missed=Count( - Case( - When( - student__order__check_in__isnull=True, - then=1 - ) - ) + orders = ( + Order.objects.select_related() + .filter(session_id=pk) + .annotate( + num_attended=Count(Case(When(student__order__check_in__isnull=False, then=1))), + num_missed=Count(Case(When(student__order__check_in__isnull=True, then=1))), ) ) if active_session: - active_orders = orders.filter( - is_active=True - ).order_by( - 'student__first_name' - ) + active_orders = orders.filter(is_active=True).order_by("student__first_name") else: - active_orders = orders.filter( - is_active=True, - check_in__isnull=False - ).order_by( - 'student__first_name' - ) + active_orders = orders.filter(is_active=True, check_in__isnull=False).order_by("student__first_name") - inactive_orders = orders.filter( - is_active=False - ).order_by('-updated_at') + inactive_orders = orders.filter(is_active=False).order_by("-updated_at") - no_show_orders = orders.filter( - is_active=True, - check_in__isnull=True - ) + no_show_orders = orders.filter(is_active=True, check_in__isnull=True) - checked_in_orders = orders.filter( - is_active=True, - check_in__isnull=False - ) + checked_in_orders = orders.filter(is_active=True, check_in__isnull=False) # Genders gender_count = sorted( - list(dict( - list( - Counter( - e.student.get_clean_gender() for e in active_orders - ).items() - ) - ).items()), - key=operator.itemgetter(1) + list(dict(list(Counter(e.student.get_clean_gender() for e in active_orders).items())).items()), + key=operator.itemgetter(1), ) # Ages - ages = sorted( - list( - e.student.get_age(e.session.start_date) for e in active_orders - ) - ) + ages = sorted(list(e.student.get_age(e.session.start_date) for e in active_orders)) - age_count = sorted( - list(dict( - list( - Counter(ages).items() - ) - ).items()), - key=operator.itemgetter(0) - ) + age_count = sorted(list(dict(list(Counter(ages).items())).items()), key=operator.itemgetter(0)) # age_count = sorted( # dict( @@ -615,26 +396,22 @@ def session_check_in(request, pk, template_name="session-check-in.html"): # ) # Average Age - average_age = int( - round( - sum(ages) / float(len(ages)) - ) - ) if orders and ages else 0 + average_age = int(round(sum(ages) / float(len(ages)))) if orders and ages else 0 return render( request, template_name, { - 'session': session, - 'active_session': active_session, - 'active_orders': active_orders, - 'inactive_orders': inactive_orders, - 'no_show_orders': no_show_orders, - 'gender_count': gender_count, - 'age_count': age_count, - 'average_age': average_age, - 'checked_in_orders': checked_in_orders, - } + "session": session, + "active_session": active_session, + "active_orders": active_orders, + "inactive_orders": inactive_orders, + "no_show_orders": no_show_orders, + "gender_count": gender_count, + "age_count": age_count, + "average_age": average_age, + "checked_in_orders": checked_in_orders, + }, ) @@ -643,18 +420,12 @@ def session_check_in(request, pk, template_name="session-check-in.html"): def session_check_in_mentors(request, pk, template_name="session-check-in-mentors.html"): if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) - return redirect('weallcode-home') + messages.error(request, "You do not have permission to access this page.") + return redirect("weallcode-home") - if request.method == 'POST': - if 'order_id' in request.POST: - order = get_object_or_404( - MentorOrder, - id=request.POST['order_id'] - ) + if request.method == "POST": + if "order_id" in request.POST: + order = get_object_or_404(MentorOrder, id=request.POST["order_id"]) if order.check_in: order.check_in = None @@ -663,10 +434,7 @@ def session_check_in_mentors(request, pk, template_name="session-check-in-mentor order.save() else: - messages.error( - request, - 'Invalid Order' - ) + messages.error(request, "Invalid Order") session = get_object_or_404(Session, pk=pk) @@ -677,45 +445,28 @@ def session_check_in_mentors(request, pk, template_name="session-check-in-mentor orders = MentorOrder.objects.select_related().filter(session_id=pk) if active_session: - active_orders = orders.filter( - is_active=True - ).order_by( - 'mentor__user__first_name' - ) + active_orders = orders.filter(is_active=True).order_by("mentor__user__first_name") else: - active_orders = orders.filter( - is_active=True, - check_in__isnull=False - ).order_by( - 'mentor__user__first_name' - ) + active_orders = orders.filter(is_active=True, check_in__isnull=False).order_by("mentor__user__first_name") - inactive_orders = orders.filter( - is_active=False - ).order_by('-updated_at') + inactive_orders = orders.filter(is_active=False).order_by("-updated_at") - no_show_orders = orders.filter( - is_active=True, - check_in__isnull=True - ) + no_show_orders = orders.filter(is_active=True, check_in__isnull=True) - checked_in_orders = orders.filter( - is_active=True, - check_in__isnull=False - ) + checked_in_orders = orders.filter(is_active=True, check_in__isnull=False) return render( request, template_name, { - 'session': session, - 'active_session': active_session, - 'active_orders': active_orders, - 'inactive_orders': inactive_orders, - 'no_show_orders': no_show_orders, - 'checked_in_orders': checked_in_orders, - } + "session": session, + "active_session": active_session, + "active_orders": active_orders, + "inactive_orders": inactive_orders, + "no_show_orders": no_show_orders, + "checked_in_orders": checked_in_orders, + }, ) @@ -725,69 +476,41 @@ def session_donations(request, pk, template_name="session-donations.html"): # TODO: we should really turn this into a decorator if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) - return redirect('account_home') + messages.error(request, "You do not have permission to access this page.") + return redirect("account_home") session = get_object_or_404(Session, pk=pk) - default_form = DonationForm(initial={'session': session}) - default_form.fields['user'].queryset = User.objects.filter( - id__in=Order.objects.filter( - session=session - ).values_list( - 'guardian__user__id', flat=True - ) + default_form = DonationForm(initial={"session": session}) + default_form.fields["user"].queryset = User.objects.filter( + id__in=Order.objects.filter(session=session).values_list("guardian__user__id", flat=True) ) form = default_form donations = Donation.objects.filter(session=session) - if request.method == 'POST': + if request.method == "POST": form = DonationForm(request.POST) if form.is_valid(): form.save() form = default_form - messages.success( - request, - 'Donation added!' - ) + messages.success(request, "Donation added!") - return render( - request, - template_name, - { - 'form': form, - 'session': session, - 'donations': donations - } - ) + return render(request, template_name, {"form": form, "session": session, "donations": donations}) @login_required @never_cache -def meeting_check_in( - request, - meeting_id, - template_name="meeting-check-in.html" -): +def meeting_check_in(request, meeting_id, template_name="meeting-check-in.html"): if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) - return redirect('account_home') + messages.error(request, "You do not have permission to access this page.") + return redirect("account_home") - if request.method == 'POST': - if 'order_id' in request.POST: - order = get_object_or_404( - MeetingOrder, - id=request.POST['order_id'] - ) + if request.method == "POST": + if "order_id" in request.POST: + order = get_object_or_404(MeetingOrder, id=request.POST["order_id"]) if order.check_in: order.check_in = None @@ -796,161 +519,128 @@ def meeting_check_in( order.save() else: - messages.error(request, 'Invalid Order') + messages.error(request, "Invalid Order") - orders = MeetingOrder.objects.select_related().filter( - meeting=meeting_id - ).order_by( - 'mentor__user__first_name' - ) + orders = MeetingOrder.objects.select_related().filter(meeting=meeting_id).order_by("mentor__user__first_name") - active_orders = orders.filter( - is_active=True - ) + active_orders = orders.filter(is_active=True) - inactive_orders = orders.filter( - is_active=False - ) + inactive_orders = orders.filter(is_active=False) - checked_in = orders.filter( - is_active=True, - check_in__isnull=False - ) + checked_in = orders.filter(is_active=True, check_in__isnull=False) return render( request, template_name, - { - 'active_orders': active_orders, - 'inactive_orders': inactive_orders, - 'checked_in': checked_in, - } + {"active_orders": active_orders, "inactive_orders": inactive_orders, "checked_in": checked_in,}, ) @never_cache def session_announce_mentors(request, pk): if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) - return redirect('home') + messages.error(request, "You do not have permission to access this page.") + return redirect("home") session_obj = get_object_or_404(Session, pk=pk) if not session_obj.announced_date_mentors: merge_data = {} merge_global_data = { - 'class_code': session_obj.course.code, - 'class_title': session_obj.course.title, - 'class_description': session_obj.course.description, - 'class_start_date': arrow.get(session_obj.mentor_start_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_start_time': arrow.get(session_obj.mentor_start_date).to('local').format('h:mma'), - 'class_end_date': arrow.get(session_obj.end_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_end_time': arrow.get(session_obj.end_date).to('local').format('h:mma'), - 'minimum_age': session_obj.minimum_age, - 'maximum_age': session_obj.maximum_age, - 'class_location_name': session_obj.location.name, - 'class_location_address': session_obj.location.address, - 'class_location_city': session_obj.location.city, - 'class_location_state': session_obj.location.state, - 'class_location_zip': session_obj.location.zip, - 'class_additional_info': session_obj.additional_info, - 'class_url': f"{settings.SITE_URL}{session_obj.get_absolute_url()}", - 'class_calendar_url': f"{settings.SITE_URL}{session_obj.get_calendar_url()}", + "class_code": session_obj.course.code, + "class_title": session_obj.course.title, + "class_description": session_obj.course.description, + "class_start_date": arrow.get(session_obj.mentor_start_date).to("local").format("dddd, MMMM D, YYYY"), + "class_start_time": arrow.get(session_obj.mentor_start_date).to("local").format("h:mma"), + "class_end_date": arrow.get(session_obj.end_date).to("local").format("dddd, MMMM D, YYYY"), + "class_end_time": arrow.get(session_obj.end_date).to("local").format("h:mma"), + "minimum_age": session_obj.minimum_age, + "maximum_age": session_obj.maximum_age, + "class_location_name": session_obj.location.name, + "class_location_address": session_obj.location.address, + "class_location_city": session_obj.location.city, + "class_location_state": session_obj.location.state, + "class_location_zip": session_obj.location.zip, + "class_additional_info": session_obj.additional_info, + "class_url": f"{settings.SITE_URL}{session_obj.get_absolute_url()}", + "class_calendar_url": f"{settings.SITE_URL}{session_obj.get_calendar_url()}", } recipients = [] # send mentor announcements - mentors = Mentor.objects.filter( - is_active=True, - user__is_active=True, - ) + mentors = Mentor.objects.filter(is_active=True, user__is_active=True,) for mentor in mentors: recipients.append(mentor.user.email) merge_data[mentor.user.email] = { - 'first_name': mentor.user.first_name, - 'last_name': mentor.user.last_name, + "first_name": mentor.user.first_name, + "last_name": mentor.user.last_name, } email( - subject='New We All Code class date announced! Come mentor!', - template_name='class-announcement-mentor', + subject="New We All Code class date announced! Come mentor!", + template_name="class-announcement-mentor", merge_data=merge_data, merge_global_data=merge_global_data, recipients=recipients, - preheader='Help us make a huge difference! A brand new class was just announced.', + preheader="Help us make a huge difference! A brand new class was just announced.", unsub_group_id=settings.SENDGRID_UNSUB_CLASSANNOUNCE, ) session_obj.announced_date_mentors = timezone.now() session_obj.save() - messages.success( - request, - f'Session announced to {mentors.count()} mentors.' - ) + messages.success(request, f"Session announced to {mentors.count()} mentors.") else: - messages.warning( - request, - f'Session already announced.' - ) + messages.warning(request, f"Session already announced.") - return redirect('cdc-admin') + return redirect("cdc-admin") @never_cache def session_announce_guardians(request, pk): if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) - return redirect('home') + messages.error(request, "You do not have permission to access this page.") + return redirect("home") session_obj = get_object_or_404(Session, pk=pk) if not session_obj.announced_date_guardians: merge_data = {} merge_global_data = { - 'class_code': session_obj.course.code, - 'class_title': session_obj.course.title, - 'class_description': session_obj.course.description, - 'class_start_date': arrow.get(session_obj.start_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_start_time': arrow.get(session_obj.start_date).to('local').format('h:mma'), - 'class_end_date': arrow.get(session_obj.end_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_end_time': arrow.get(session_obj.end_date).to('local').format('h:mma'), - 'minimum_age': session_obj.minimum_age, - 'maximum_age': session_obj.maximum_age, - 'class_location_name': session_obj.location.name, - 'class_location_address': session_obj.location.address, - 'class_location_city': session_obj.location.city, - 'class_location_state': session_obj.location.state, - 'class_location_zip': session_obj.location.zip, - 'class_additional_info': session_obj.additional_info, - 'class_url': f"{settings.SITE_URL}{session_obj.get_absolute_url()}", - 'class_calendar_url': f"{settings.SITE_URL}{session_obj.get_calendar_url()}", + "class_code": session_obj.course.code, + "class_title": session_obj.course.title, + "class_description": session_obj.course.description, + "class_start_date": arrow.get(session_obj.start_date).to("local").format("dddd, MMMM D, YYYY"), + "class_start_time": arrow.get(session_obj.start_date).to("local").format("h:mma"), + "class_end_date": arrow.get(session_obj.end_date).to("local").format("dddd, MMMM D, YYYY"), + "class_end_time": arrow.get(session_obj.end_date).to("local").format("h:mma"), + "minimum_age": session_obj.minimum_age, + "maximum_age": session_obj.maximum_age, + "class_location_name": session_obj.location.name, + "class_location_address": session_obj.location.address, + "class_location_city": session_obj.location.city, + "class_location_state": session_obj.location.state, + "class_location_zip": session_obj.location.zip, + "class_additional_info": session_obj.additional_info, + "class_url": f"{settings.SITE_URL}{session_obj.get_absolute_url()}", + "class_calendar_url": f"{settings.SITE_URL}{session_obj.get_calendar_url()}", } recipients = [] - guardians = Guardian.objects.filter( - is_active=True, - user__is_active=True, - ) + guardians = Guardian.objects.filter(is_active=True, user__is_active=True,) for guardian in guardians: recipients.append(guardian.user.email) merge_data[guardian.user.email] = { - 'first_name': guardian.user.first_name, - 'last_name': guardian.user.last_name, + "first_name": guardian.user.first_name, + "last_name": guardian.user.last_name, } email( - subject='New We All Code class date announced!', - template_name='class-announcement-guardian', + subject="New We All Code class date announced!", + template_name="class-announcement-guardian", merge_data=merge_data, merge_global_data=merge_global_data, recipients=recipients, @@ -961,18 +651,12 @@ def session_announce_guardians(request, pk): session_obj.announced_date_guardians = timezone.now() session_obj.save() - messages.success( - request, - f'Session announced to {guardians.count()} guardians!' - ) + messages.success(request, f"Session announced to {guardians.count()} guardians!") else: - messages.warning( - request, - 'Session already announced.' - ) + messages.warning(request, "Session already announced.") - return redirect('cdc-admin') + return redirect("cdc-admin") @csrf_exempt @@ -982,36 +666,24 @@ def check_system(request): runUpdate = True responseString = "" cmdString = ( - 'sh -c "$(curl -fsSL ' - 'https://raw.githubusercontent.com/CoderDojoChi' - '/linux-update/master/update.sh)"' + 'sh -c "$(curl -fsSL ' "https://raw.githubusercontent.com/CoderDojoChi" '/linux-update/master/update.sh)"' ) halfday = timedelta(hours=12) # halfday = timedelta(seconds=15) - if ( - Session.objects.filter( - is_active=True, - start_date__lte=timezone.now(), - ).count() - ): + if Session.objects.filter(is_active=True, start_date__lte=timezone.now(),).count(): runUpdate = False # uuid is posted from the computer using a bash script # see: # https://raw.githubusercontent.com/CoderDojoChi # /linux-update/master/etc/init.d/coderdojochi-phonehome - uuid = request.POST.get('uuid') + uuid = request.POST.get("uuid") if uuid: equipmentType = EquipmentType.objects.get(name="Laptop") if equipmentType: - equipment, created = Equipment.objects.get_or_create( - uuid=uuid, - defaults={ - 'equipment_type': equipmentType - } - ) + equipment, created = Equipment.objects.get_or_create(uuid=uuid, defaults={"equipment_type": equipmentType}) # check for blank values of last_system_update. # If blank, assume we need to run it @@ -1019,12 +691,8 @@ def check_system(request): equipment.force_update_on_next_boot = True # do we need to update? - if ( - runUpdate and - ( - equipment.force_update_on_next_boot or - (timezone.now() - equipment.last_system_update > halfday) - ) + if runUpdate and ( + equipment.force_update_on_next_boot or (timezone.now() - equipment.last_system_update > halfday) ): responseString = cmdString equipment.last_system_update = timezone.now() diff --git a/coderdojochi/settings.py b/coderdojochi/settings.py index 770a3566..1cdfe18a 100644 --- a/coderdojochi/settings.py +++ b/coderdojochi/settings.py @@ -29,26 +29,23 @@ # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = env( - 'SECRET_KEY', - default='!!!SET SECRET_KEY!!!', -) +SECRET_KEY = env("SECRET_KEY", default="!!!SET SECRET_KEY!!!",) # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = env.bool('DEBUG', default=False) +DEBUG = env.bool("DEBUG", default=False) # reCAPTCHA -RECAPTCHA_PUBLIC_KEY = env('RECAPTCHA_PUBLIC_KEY', default='') -RECAPTCHA_PRIVATE_KEY = env('RECAPTCHA_PRIVATE_KEY', default='') -RECAPTCHA_REQUIRED_SCORE = env('RECAPTCHA_REQUIRED_SCORE', default=0.85) +RECAPTCHA_PUBLIC_KEY = env("RECAPTCHA_PUBLIC_KEY", default="") +RECAPTCHA_PRIVATE_KEY = env("RECAPTCHA_PRIVATE_KEY", default="") +RECAPTCHA_REQUIRED_SCORE = env("RECAPTCHA_REQUIRED_SCORE", default=0.85) # SECURITY # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect -SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) +SECURE_SSL_REDIRECT = env.bool("DJANGO_SECURE_SSL_REDIRECT", default=True) if SECURE_SSL_REDIRECT: # https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header - SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') + SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure SESSION_COOKIE_SECURE = True # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly @@ -62,101 +59,94 @@ # set this to 60 seconds first and then to 518400 once you prove the former works SECURE_HSTS_SECONDS = 518400 # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains - SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool('DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS', default=True) + SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool("DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True) # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload - SECURE_HSTS_PRELOAD = env.bool('DJANGO_SECURE_HSTS_PRELOAD', default=True) + SECURE_HSTS_PRELOAD = env.bool("DJANGO_SECURE_HSTS_PRELOAD", default=True) # https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff - SECURE_CONTENT_TYPE_NOSNIFF = env.bool('DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True) + SECURE_CONTENT_TYPE_NOSNIFF = env.bool("DJANGO_SECURE_CONTENT_TYPE_NOSNIFF", default=True) # https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter SECURE_BROWSER_XSS_FILTER = True # https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options - X_FRAME_OPTIONS = 'DENY' + X_FRAME_OPTIONS = "DENY" # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.humanize', - - 'django.contrib.sites', - 'django.contrib.redirects', - 'django.contrib.sitemaps', - + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.humanize", + "django.contrib.sites", + "django.contrib.redirects", + "django.contrib.sitemaps", # vendor - # allauth - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - 'allauth.socialaccount.providers.facebook', - 'allauth.socialaccount.providers.google', - - 'fullurl', - 'bootstrap3', - 'django_cleanup', - 'anymail', - 'html5', - 'loginas', - 'stdimage', - 'import_export', - 'django_nose', - 'meta', - 'captcha', - + "allauth", + "allauth.account", + "allauth.socialaccount", + "allauth.socialaccount.providers.facebook", + "allauth.socialaccount.providers.google", + "fullurl", + "bootstrap3", + "django_cleanup", + "anymail", + "html5", + "loginas", + "stdimage", + "import_export", + "django_nose", + "meta", + "captcha", # apps - 'accounts', - 'coderdojochi', - 'weallcode', + "accounts", + "coderdojochi", + "weallcode", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - - 'django.contrib.redirects.middleware.RedirectFallbackMiddleware', - - 'coderdojochi.middleware.HandleExceptionMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django.contrib.redirects.middleware.RedirectFallbackMiddleware", + "coderdojochi.middleware.HandleExceptionMiddleware", ] -ROOT_URLCONF = 'coderdojochi.urls' +ROOT_URLCONF = "coderdojochi.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'accounts/templates/'), - os.path.join(BASE_DIR, 'weallcode/templates/'), - os.path.join(BASE_DIR, 'coderdojochi/templates/'), - os.path.join(BASE_DIR, 'coderdojochi/templates/dashboard/'), - os.path.join(BASE_DIR, 'coderdojochi/emailtemplates/'), - os.path.join(BASE_DIR, 'coderdojochi/mentors/templates'), + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(BASE_DIR, "accounts/templates/"), + os.path.join(BASE_DIR, "weallcode/templates/"), + os.path.join(BASE_DIR, "coderdojochi/templates/"), + os.path.join(BASE_DIR, "coderdojochi/templates/dashboard/"), + os.path.join(BASE_DIR, "coderdojochi/emailtemplates/"), + os.path.join(BASE_DIR, "coderdojochi/mentors/templates"), ], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", # Project - 'coderdojochi.context_processors.main_config_processor', + "coderdojochi.context_processors.main_config_processor", ], - 'debug': DEBUG, + "debug": DEBUG, }, }, ] -WSGI_APPLICATION = 'coderdojochi.wsgi.application' +WSGI_APPLICATION = "coderdojochi.wsgi.application" # Database @@ -166,91 +156,83 @@ DATABASES = {"default": env.db()} else: DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': os.environ.get('POSTGRES_DB'), - 'USER': os.environ.get('POSTGRES_USER'), - 'PASSWORD': os.environ.get('POSTGRES_PASSWORD'), - 'HOST': os.environ.get('POSTGRES_HOST'), - 'PORT': os.environ.get('POSTGRES_PORT'), - } + "default": { + "ENGINE": "django.db.backends.postgresql_psycopg2", + "NAME": os.environ.get("POSTGRES_DB"), + "USER": os.environ.get("POSTGRES_USER"), + "PASSWORD": os.environ.get("POSTGRES_PASSWORD"), + "HOST": os.environ.get("POSTGRES_HOST"), + "PORT": os.environ.get("POSTGRES_PORT"), } + } DATABASES["default"]["ATOMIC_REQUESTS"] = True # Change 'default' database configuration with $DATABASE_URL. -DATABASES['default'].update(dj_database_url.config(conn_max_age=500, ssl_require=True)) +DATABASES["default"].update(dj_database_url.config(conn_max_age=500, ssl_require=True)) # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, + {"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",}, + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",}, + {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",}, + {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",}, ] # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'America/Chicago' +LANGUAGE_CODE = "en-us" +TIME_ZONE = "America/Chicago" USE_I18N = True USE_L10N = True USE_TZ = True SITE_ID = 1 -SITE_NAME = env('SITE_NAME', default=None) -SITE_URL = env('SITE_URL', default=None) +SITE_NAME = env("SITE_NAME", default=None) +SITE_URL = env("SITE_URL", default=None) # Honor the 'X-Forwarded-Proto' header for request.is_secure() -SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") # Allow all host headers -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] -INSTALLED_APPS += ['django.contrib.staticfiles'] +INSTALLED_APPS += ["django.contrib.staticfiles"] if DEBUG: # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ # STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') - STATIC_URL = '/static/' + STATIC_URL = "/static/" # Extra places for collectstatic to find static files. STATICFILES_DIRS = [ - os.path.join(PROJECT_ROOT, 'static'), - os.path.join(BASE_DIR, 'accounts/static'), - os.path.join(BASE_DIR, 'weallcode/static'), + os.path.join(PROJECT_ROOT, "static"), + os.path.join(BASE_DIR, "accounts/static"), + os.path.join(BASE_DIR, "weallcode/static"), ] # Media files - MEDIA_ROOT = os.path.join(BASE_DIR, 'media') - MEDIA_URL = '/media/' + MEDIA_ROOT = os.path.join(BASE_DIR, "media") + MEDIA_URL = "/media/" else: # STORAGES # ------------------------------------------------------------------------------ # https://django-storages.readthedocs.io/en/latest/#installation - INSTALLED_APPS += ['storages'] # noqa F405 + INSTALLED_APPS += ["storages"] # noqa F405 # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings - AWS_ACCESS_KEY_ID = env('AWS_ACCESS_KEY_ID') + AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID") # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings - AWS_SECRET_ACCESS_KEY = env('AWS_SECRET_ACCESS_KEY') + AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY") # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings - AWS_STORAGE_BUCKET_NAME = env('AWS_STORAGE_BUCKET_NAME') + AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME") # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings AWS_AUTO_CREATE_BUCKET = True # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings @@ -259,22 +241,22 @@ _AWS_EXPIRY = 60 * 60 * 24 * 7 # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings AWS_S3_OBJECT_PARAMETERS = { - 'CacheControl': f'max-age={_AWS_EXPIRY}, s-maxage={_AWS_EXPIRY}, must-revalidate', + "CacheControl": f"max-age={_AWS_EXPIRY}, s-maxage={_AWS_EXPIRY}, must-revalidate", } # STATIC # ------------------------ - STATICFILES_STORAGE = 'coderdojochi.settings.StaticRootS3BotoStorage' - STATIC_URL = f'https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/static/' + STATICFILES_STORAGE = "coderdojochi.settings.StaticRootS3BotoStorage" + STATIC_URL = f"https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/static/" # MEDIA # ------------------------------------------------------------------------------ # region http://stackoverflow.com/questions/10390244/ - from storages.backends.s3boto3 import S3Boto3Storage, SpooledTemporaryFile # noqa E402 from django.contrib.staticfiles.storage import ManifestFilesMixin - # ManifestFilesSafeMixin = lambda: ManifestFilesMixin(manifest_strict=False) + from storages.backends.s3boto3 import S3Boto3Storage, SpooledTemporaryFile # noqa E402 + # ManifestFilesSafeMixin = lambda: ManifestFilesMixin(manifest_strict=False) # Taken from an issue in django-storages: # https://github.com/jschneier/django-storages/issues/382#issuecomment-377174808 class CustomS3Storage(ManifestFilesMixin, S3Boto3Storage): @@ -299,80 +281,75 @@ def _save_content(self, obj, content, parameters): if not content_autoclose.closed: content_autoclose.close() - def StaticRootS3BotoStorage(): return CustomS3Storage(location='static') + def StaticRootS3BotoStorage(): + return CustomS3Storage(location="static") - def MediaRootS3BotoStorage(): return S3Boto3Storage(location='media', file_overwrite=False) + def MediaRootS3BotoStorage(): + return S3Boto3Storage(location="media", file_overwrite=False) - DEFAULT_FILE_STORAGE = 'coderdojochi.settings.MediaRootS3BotoStorage' - MEDIA_URL = f'https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/media/' + DEFAULT_FILE_STORAGE = "coderdojochi.settings.MediaRootS3BotoStorage" + MEDIA_URL = f"https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/media/" # endregion AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - + "django.contrib.auth.backends.ModelBackend", # `allauth` specific authentication methods, such as login by e-mail - 'allauth.account.auth_backends.AuthenticationBackend', + "allauth.account.auth_backends.AuthenticationBackend", ) -AUTH_USER_MODEL = 'coderdojochi.CDCUser' +AUTH_USER_MODEL = "coderdojochi.CDCUser" # Django Meta -META_SITE_PROTOCOL = env('META_SITE_PROTOCOL', default='https') -META_SITE_DOMAIN = env('META_SITE_DOMAIN', default='www.weallcode.org') +META_SITE_PROTOCOL = env("META_SITE_PROTOCOL", default="https") +META_SITE_DOMAIN = env("META_SITE_DOMAIN", default="www.weallcode.org") META_SITE_NAME = SITE_NAME META_USE_OG_PROPERTIES = True META_USE_TWITTER_PROPERTIES = True META_USE_GOOGLEPLUS_PROPERTIES = False META_USE_TITLE_TAG = False -META_TWITTER_SITE = env('META_TWITTER_SITE', default='@weallcode') -META_FB_APPID = env('META_SITE_DOMAIN', default='1454178301519376') -META_INCLUDE_KEYWORDS = env.list('META_INCLUDE_KEYWORDS', default=[ - 'stem', - 'code', - 'coding', - 'kids', - 'chicago', - 'chicago coding' -]) -DEFAULT_META_TITLE = env('DEFAULT_META_TITLE', default='') +META_TWITTER_SITE = env("META_TWITTER_SITE", default="@weallcode") +META_FB_APPID = env("META_SITE_DOMAIN", default="1454178301519376") +META_INCLUDE_KEYWORDS = env.list( + "META_INCLUDE_KEYWORDS", default=["stem", "code", "coding", "kids", "chicago", "chicago coding"] +) +DEFAULT_META_TITLE = env("DEFAULT_META_TITLE", default="") # django allauth -LOGIN_REDIRECT_URL = '/account' -LOGIN_URL = '/account/login' +LOGIN_REDIRECT_URL = "/account" +LOGIN_URL = "/account/login" ACCOUNT_EMAIL_REQUIRED = True -ACCOUNT_AUTHENTICATION_METHOD = 'email' +ACCOUNT_AUTHENTICATION_METHOD = "email" ACCOUNT_USERNAME_REQUIRED = False -ACCOUNT_SIGNUP_FORM_CLASS = 'coderdojochi.forms.SignupForm' -SOCIALACCOUNT_ADAPTER = 'coderdojochi.social_account_adapter.SocialAccountAdapter' +ACCOUNT_SIGNUP_FORM_CLASS = "coderdojochi.forms.SignupForm" +SOCIALACCOUNT_ADAPTER = "coderdojochi.social_account_adapter.SocialAccountAdapter" # Email ANYMAIL = { - 'SENDGRID_API_KEY': env('SENDGRID_API_KEY'), + "SENDGRID_API_KEY": env("SENDGRID_API_KEY"), } -EMAIL_BACKEND = 'anymail.backends.sendgrid.EmailBackend' -DEFAULT_FROM_EMAIL = env('DEFAULT_FROM_EMAIL') -CONTACT_EMAIL = env('CONTACT_EMAIL') -SENDGRID_UNSUB_CLASSANNOUNCE = env.int('SENDGRID_UNSUB_CLASSANNOUNCE') +EMAIL_BACKEND = "anymail.backends.sendgrid.EmailBackend" +DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL") +CONTACT_EMAIL = env("CONTACT_EMAIL") +SENDGRID_UNSUB_CLASSANNOUNCE = env.int("SENDGRID_UNSUB_CLASSANNOUNCE") # Slack -SLACK_WEBHOOK_URL = env('SLACK_WEBHOOK_URL') -SLACK_ALERTS_CHANNEL = env('SLACK_ALERTS_CHANNEL', default=None) +SLACK_WEBHOOK_URL = env("SLACK_WEBHOOK_URL") +SLACK_ALERTS_CHANNEL = env("SLACK_ALERTS_CHANNEL", default=None) # Sentry -SENTRY_DSN = env('SENTRY_DSN') +SENTRY_DSN = env("SENTRY_DSN") sentry_sdk.init( dsn=SENTRY_DSN, integrations=[DjangoIntegration()], - # If you wish to associate users to errors (assuming you are using # django.contrib.auth) you may enable sending PII data. - send_default_pii=True + send_default_pii=True, ) @@ -384,18 +361,14 @@ def custom_show_toolbar(request): DEBUG_TOOLBAR_PATCH_SETTINGS = False - INSTALLED_APPS += ( - 'debug_toolbar', - ) + INSTALLED_APPS += ("debug_toolbar",) - MIDDLEWARE += ( - 'debug_toolbar.middleware.DebugToolbarMiddleware', - ) + MIDDLEWARE += ("debug_toolbar.middleware.DebugToolbarMiddleware",) DEBUG_TOOLBAR_CONFIG = { - 'SHOW_TOOLBAR_CALLBACK': custom_show_toolbar, - 'TAG': 'div', - 'ENABLE_STACKTRACES': True, + "SHOW_TOOLBAR_CALLBACK": custom_show_toolbar, + "TAG": "div", + "ENABLE_STACKTRACES": True, } diff --git a/coderdojochi/signals_handlers.py b/coderdojochi/signals_handlers.py index 68b7baa2..ec3bf624 100644 --- a/coderdojochi/signals_handlers.py +++ b/coderdojochi/signals_handlers.py @@ -4,11 +4,8 @@ from django.core.exceptions import ObjectDoesNotExist from django.db.models.signals import pre_save from django.dispatch import receiver -from django.shortcuts import get_object_or_404 -import arrow - -from coderdojochi.models import Donation, Mentor +from coderdojochi.models import Mentor from coderdojochi.util import email @@ -27,21 +24,21 @@ def avatar_updated_handler(sender, instance, **kwargs): instance.avatar_approved = False img = MIMEImage(instance.avatar.read()) - img.add_header('Content-Id', 'avatar') + img.add_header("Content-Id", "avatar") img.add_header("Content-Disposition", "inline", filename="avatar") email( subject=f"{instance.user.first_name} {instance.user.last_name} | Mentor Avatar Changed", - template_name='avatar-changed-mentor', + template_name="avatar-changed-mentor", merge_global_data={ - 'first_name': instance.user.first_name, - 'last_name': instance.user.last_name, - 'image': 'avatar', - 'approve_url': f"{settings.SITE_URL}{instance.get_approve_avatar_url()}", - 'reject_url': f"{settings.SITE_URL}{instance.get_reject_avatar_url()}", + "first_name": instance.user.first_name, + "last_name": instance.user.last_name, + "image": "avatar", + "approve_url": f"{settings.SITE_URL}{instance.get_approve_avatar_url()}", + "reject_url": f"{settings.SITE_URL}{instance.get_reject_avatar_url()}", }, recipients=[settings.CONTACT_EMAIL], - preheader='Mentor Avatar Changed', + preheader="Mentor Avatar Changed", attachments=[img], - mixed_subtype='related', + mixed_subtype="related", ) diff --git a/coderdojochi/social_account_adapter.py b/coderdojochi/social_account_adapter.py index f052a56a..37580e23 100644 --- a/coderdojochi/social_account_adapter.py +++ b/coderdojochi/social_account_adapter.py @@ -1,8 +1,10 @@ -from allauth.socialaccount.adapter import DefaultSocialAccountAdapter from django.contrib.auth import get_user_model +from allauth.socialaccount.adapter import DefaultSocialAccountAdapter + User = get_user_model() + class SocialAccountAdapter(DefaultSocialAccountAdapter): def pre_social_login(self, request, sociallogin): """ @@ -24,7 +26,7 @@ def pre_social_login(self, request, sociallogin): # e.g. facebook accounts # with mobile numbers only, but allauth takes care of this case # so just ignore it - if 'email' not in sociallogin.account.extra_data: + if "email" not in sociallogin.account.extra_data: return # check if given email address already exists. @@ -32,7 +34,7 @@ def pre_social_login(self, request, sociallogin): try: # email = sociallogin.account.extra_data['email'].lower() # email_address = EmailAddress.objects.get(email__iexact=email) - email = sociallogin.account.extra_data['email'].lower() + email = sociallogin.account.extra_data["email"].lower() user = User.objects.get(email__iexact=email) # if it does not, let allauth take care of this new social account diff --git a/coderdojochi/templatetags/coderdojochi_extras.py b/coderdojochi/templatetags/coderdojochi_extras.py index c37c15be..0210afd9 100644 --- a/coderdojochi/templatetags/coderdojochi_extras.py +++ b/coderdojochi/templatetags/coderdojochi_extras.py @@ -16,58 +16,37 @@ def subtract(value, arg): @register.simple_tag(takes_context=False) def student_session_order_count(student, session): - orders_count = Order.objects.filter( - student=student, - session=session - ).count() + orders_count = Order.objects.filter(student=student, session=session).count() return orders_count @register.simple_tag(takes_context=True) def student_register_link(context, student, session): - orders = Order.objects.filter( - student=student, - session=session, - is_active=True - ) + orders = Order.objects.filter(student=student, session=session, is_active=True) - url = reverse('session-sign-up', kwargs={'pk': session.id, 'student_id': student.id, }) + url = reverse("session-sign-up", kwargs={"pk": session.id, "student_id": student.id,}) - button_tag = 'a' - button_modifier = '' - button_additional_attributes = '' - button_msg = 'Enroll' - button_href = f'href={url}' + button_tag = "a" + button_modifier = "" + button_additional_attributes = "" + button_msg = "Enroll" + button_href = f"href={url}" if orders.count(): button_modifier = "tertiary" button_msg = "Can't make it" - elif ( - not student.is_within_age_range( - session.minimum_age, - session.maximum_age, - session.start_date - ) or - not student.is_within_gender_limitation( - session.gender_limitation - ) - ): - button_modifier = 'btn-default' - button_additional_attributes = 'disabled' - button_tag = 'span' - - if ( - not student.is_within_age_range( - session.minimum_age, - session.maximum_age, - session.start_date - ) and - not student.is_within_gender_limitation( - session.gender_limitation - ) - ): + elif not student.is_within_age_range( + session.minimum_age, session.maximum_age, session.start_date + ) or not student.is_within_gender_limitation(session.gender_limitation): + button_modifier = "btn-default" + button_additional_attributes = "disabled" + button_tag = "span" + + if not student.is_within_age_range( + session.minimum_age, session.maximum_age, session.start_date + ) and not student.is_within_gender_limitation(session.gender_limitation): title = "Limited event." message = ( f"Sorry, this class is limited to {session.gender_limitation}s between {session.minimum_age} " @@ -79,13 +58,7 @@ def student_register_link(context, student, session): f'data-content="{message}" data-original-title="{title}"' ) - elif ( - not student.is_within_age_range( - session.minimum_age, - session.maximum_age, - session.start_date - ) - ): + elif not student.is_within_age_range(session.minimum_age, session.maximum_age, session.start_date): title = "Age-limited event." message = ( f"Sorry, this class is limited to student between ages {session.minimum_age} and " @@ -96,14 +69,8 @@ def student_register_link(context, student, session): f'data-content="{message}" data-original-title="{title}"' ) - elif ( - not student.is_within_gender_limitation( - session.gender_limitation - ) - ): - title = "{gender}-only event.".format( - gender='Girls' if session.gender_limitation == 'female' else 'Boys' - ) + elif not student.is_within_gender_limitation(session.gender_limitation): + title = "{gender}-only event.".format(gender="Girls" if session.gender_limitation == "female" else "Boys") message = f"Sorry, this class is limited to {session.gender_limitation}s this time around." button_href = ( 'data-trigger="hover" data-placement="top" data-toggle="popover" title="" ' @@ -125,10 +92,10 @@ def student_age(student, date): @register.simple_tag(takes_context=True) -def menu_is_active(context, pattern_or_urlname, css_class='active'): +def menu_is_active(context, pattern_or_urlname, css_class="active"): try: - pattern = '^' + reverse(pattern_or_urlname) + pattern = "^" + reverse(pattern_or_urlname) except NoReverseMatch: pattern = pattern_or_urlname - return css_class if re.search(pattern, context['request'].path) else '' + return css_class if re.search(pattern, context["request"].path) else "" diff --git a/coderdojochi/tests/test_mentor_updates.py b/coderdojochi/tests/test_mentor_updates.py index 4804d215..8294a010 100644 --- a/coderdojochi/tests/test_mentor_updates.py +++ b/coderdojochi/tests/test_mentor_updates.py @@ -1,5 +1,3 @@ -from unittest import TestCase - from django.test import TransactionTestCase import mock @@ -8,7 +6,7 @@ class TestMentorAvatarUpdates(TransactionTestCase): - @mock.patch('coderdojochi.signals_handlers.EmailMultiAlternatives') + @mock.patch("coderdojochi.signals_handlers.EmailMultiAlternatives") def test_new_mentor_no_avatar(self, mock_email): mentor = Mentor.objects.create() self.fail() diff --git a/coderdojochi/tests/test_password_session.py b/coderdojochi/tests/test_password_session.py index a1b2c9d3..aeb8dc29 100644 --- a/coderdojochi/tests/test_password_session.py +++ b/coderdojochi/tests/test_password_session.py @@ -1,92 +1,62 @@ -# import mock -# import sys - from django.contrib.auth import get_user_model from django.http import HttpResponseRedirect -# from django.test import RequestFactory from django.test import Client, TestCase from django.urls import reverse from coderdojochi.factories import PartnerPasswordAccessFactory, SessionFactory from coderdojochi.models import PartnerPasswordAccess -# from coderdojochi.factories import CDCUserFactory -# from coderdojochi.views import session_detail - - User = get_user_model() class TestPartnerSessionPassword(TestCase): def setUp(self): - self.partner_session = SessionFactory.create(password='124') + self.partner_session = SessionFactory.create(password="124") self.url_kwargs = { - 'year': self.partner_session.start_date.year, - 'month': self.partner_session.start_date.month, - 'day': self.partner_session.start_date.day, - 'slug': self.partner_session.course.slug, - 'session_id': self.partner_session.id + "year": self.partner_session.start_date.year, + "month": self.partner_session.start_date.month, + "day": self.partner_session.start_date.day, + "slug": self.partner_session.course.slug, + "session_id": self.partner_session.id, } self.client = Client() - self.url = reverse('session_password', kwargs=self.url_kwargs) + self.url = reverse("session_password", kwargs=self.url_kwargs) def test_session_password_invalid_password(self): - response = self.client.post(self.url, data={'password': 'abc'}) - self.assertContains(response, 'Invalid password.') + response = self.client.post(self.url, data={"password": "abc"}) + self.assertContains(response, "Invalid password.") def test_session_password_no_password(self): - response = self.client.post(self.url, data={'password': ''}) - self.assertContains(response, 'Must enter a password.') + response = self.client.post(self.url, data={"password": ""}) + self.assertContains(response, "Must enter a password.") def test_session_password_valid_password_unauthed(self): - response = self.client.post( - self.url, - data={ - 'password': self.partner_session.password - } - ) + response = self.client.post(self.url, data={"password": self.partner_session.password}) self.assertIsInstance(response, HttpResponseRedirect) - detail_url = reverse('session_detail', kwargs=self.url_kwargs) + detail_url = reverse("session_detail", kwargs=self.url_kwargs) self.assertEqual(response.url, detail_url) password_access_count = PartnerPasswordAccess.objects.count() self.assertEqual(password_access_count, 0) - authed_sessions = self.client.session['authed_partner_sessions'] + authed_sessions = self.client.session["authed_partner_sessions"] self.assertFalse(str(self.partner_session.id) in authed_sessions) def test_session_password_valid_password_authed(self): - user = User.objects.create_user( - 'user', - email='email@email.com', - password='pass123' - ) - self.assertTrue( - self.client.login( - email='email@email.com', - password='pass123' - ) - ) - - response = self.client.post( - self.url, - data={ - 'password': self.partner_session.password - } - ) + user = User.objects.create_user("user", email="email@email.com", password="pass123") + self.assertTrue(self.client.login(email="email@email.com", password="pass123")) + + response = self.client.post(self.url, data={"password": self.partner_session.password}) self.assertIsInstance(response, HttpResponseRedirect) - detail_url = reverse('session_detail', kwargs=self.url_kwargs) + detail_url = reverse("session_detail", kwargs=self.url_kwargs) self.assertEqual(response.url, detail_url) - partner_password_access = PartnerPasswordAccess.objects.get( - session=self.partner_session, - user=user - ) + partner_password_access = PartnerPasswordAccess.objects.get(session=self.partner_session, user=user) self.assertIsNotNone(partner_password_access) - authed_sessions = self.client.session['authed_partner_sessions'] + authed_sessions = self.client.session["authed_partner_sessions"] self.assertTrue(str(self.partner_session.id) in authed_sessions) @@ -94,61 +64,40 @@ class TestSessionDetail(TestCase): def setUp(self): super(TestSessionDetail, self).setUp() self.client = Client() - self.partner_session = SessionFactory.create(password='124') + self.partner_session = SessionFactory.create(password="124") self.url_kwargs = { - 'year': self.partner_session.start_date.year, - 'month': self.partner_session.start_date.month, - 'day': self.partner_session.start_date.day, - 'slug': self.partner_session.course.slug, - 'session_id': self.partner_session.id + "year": self.partner_session.start_date.year, + "month": self.partner_session.start_date.month, + "day": self.partner_session.start_date.day, + "slug": self.partner_session.course.slug, + "session_id": self.partner_session.id, } - self.url = reverse('session_detail', kwargs=self.url_kwargs) + self.url = reverse("session_detail", kwargs=self.url_kwargs) def test_redirect_password_unauthed(self): response = self.client.get(self.url) self.assertIsInstance(response, HttpResponseRedirect) - detail_url = reverse('session_password', kwargs=self.url_kwargs) + detail_url = reverse("session_password", kwargs=self.url_kwargs) self.assertEqual(response.url, detail_url) def test_redirect_password_authed(self): - User.objects.create_user( - 'user', - email='email@email.com', - password='pass123' - ) - self.assertTrue( - self.client.login( - email='email@email.com', - password='pass123' - ) - ) + User.objects.create_user("user", email="email@email.com", password="pass123") + self.assertTrue(self.client.login(email="email@email.com", password="pass123")) response = self.client.get(self.url) self.assertIsInstance(response, HttpResponseRedirect) - detail_url = reverse('session_password', kwargs=self.url_kwargs) + detail_url = reverse("session_password", kwargs=self.url_kwargs) self.assertEqual(response.url, detail_url) def test_redirect_password_partner_password_access(self): - user = User.objects.create_user( - 'user', - email='email@email.com', - password='pass123' - ) - self.assertTrue( - self.client.login( - email='email@email.com', - password='pass123' - ) - ) - - PartnerPasswordAccessFactory.create( - user=user, - session=self.partner_session - ) + user = User.objects.create_user("user", email="email@email.com", password="pass123") + self.assertTrue(self.client.login(email="email@email.com", password="pass123")) + + PartnerPasswordAccessFactory.create(user=user, session=self.partner_session) response = self.client.get(self.url) - detail_url = reverse('session_password', kwargs=self.url_kwargs) + detail_url = reverse("session_password", kwargs=self.url_kwargs) # don't care what its doing as long as its not # redirecting to the password url. diff --git a/coderdojochi/urls.py b/coderdojochi/urls.py index 7879e459..84f4d195 100644 --- a/coderdojochi/urls.py +++ b/coderdojochi/urls.py @@ -1,30 +1,15 @@ from django.conf import settings -from django.conf.urls import include, url +from django.conf.urls import include from django.conf.urls.static import static from django.contrib import admin -from django.contrib.auth import views as django_views from django.http import HttpResponse from django.urls import path from django.views import defaults from django.views.generic import RedirectView -from loginas import views as loginas_views - from . import old_views -from .views.meetings import ( - MeetingCalendarView, - MeetingDetailView, - MeetingsView, - meeting_announce, - meeting_sign_up, -) -from .views.profile import DojoMentorView -from .views.sessions import ( - PasswordSessionView, - SessionCalendarView, - SessionDetailView, - SessionSignUpView -) +from .views.meetings import MeetingCalendarView, MeetingDetailView, MeetingsView, meeting_announce, meeting_sign_up +from .views.sessions import PasswordSessionView, SessionCalendarView, SessionDetailView, SessionSignUpView from .views.welcome import WelcomeView admin.autodiscover() @@ -34,151 +19,165 @@ # General Pages urlpatterns += [ - path('', include('weallcode.urls')), + path("", include("weallcode.urls")), ] # Accounts urlpatterns += [ - path('account/', include('accounts.urls')), + path("account/", include("accounts.urls")), ] # Old General urlpatterns += [ - - path('old/', include([ - - # Meetings - path('meetings/', include([ - # Meetings - # /meetings/ - path('', MeetingsView.as_view(), name='meetings'), - - # Individual Meeting - # /meeting/ID/ - path('/', MeetingDetailView.as_view(), name='meeting-detail'), - - # /meeting/ID/announce/ - path('/announce/', meeting_announce, name='meeting-announce'), - - # Meeting sign up - # /meeting/ID/sign-up/ - path('/register/', meeting_sign_up, name='meeting-register'), - - # Meeting Calendar - # /meeting/ID/calendar/ - path('/calendar/', MeetingCalendarView.as_view(), name='meeting-calendar'), - ])), - - ])), - + path( + "old/", + include( + [ + # Meetings + path( + "meetings/", + include( + [ + # Meetings + # /meetings/ + path("", MeetingsView.as_view(), name="meetings"), + # Individual Meeting + # /meeting/ID/ + path("/", MeetingDetailView.as_view(), name="meeting-detail"), + # /meeting/ID/announce/ + path("/announce/", meeting_announce, name="meeting-announce"), + # Meeting sign up + # /meeting/ID/sign-up/ + path("/register/", meeting_sign_up, name="meeting-register"), + # Meeting Calendar + # /meeting/ID/calendar/ + path("/calendar/", MeetingCalendarView.as_view(), name="meeting-calendar"), + ] + ), + ), + ] + ), + ), ] # Login As urlpatterns += [ - path('dj-admin/', include('loginas.urls')), + path("dj-admin/", include("loginas.urls")), ] # Django Admin urlpatterns += [ # /dj-admin/ - path('dj-admin/', admin.site.urls), + path("dj-admin/", admin.site.urls), ] # Admin urlpatterns += [ - path('admin/', include([ - # Admin - # /admin/ - path('', old_views.cdc_admin, name='cdc-admin'), - - path('classes/', include([ - # /admin/classes/ID/stats/ - path('/stats/', old_views.session_stats, name='stats'), - - # /admin/classes/ID/check-in/ - path('/check-in/', old_views.session_check_in, name='student-check-in'), - - # /admin/classes/ID/check-in-mentors/ - path('/check-in-mentors/', old_views.session_check_in_mentors, name='mentor-check-in'), - - # /admin/classes/ID/donations/ - path('/donations/', old_views.session_donations, name='donations'), - ])), - - path('meetings/', include([ - # /admin/meeting/ID/check-in/ - path('/check-in/', old_views.meeting_check_in, name='meeting-check-in'), - ])), - - # Admin Check System - # /admin/checksystem/ - path('checksystem/', old_views.check_system, name='check-system'), - ])) + path( + "admin/", + include( + [ + # Admin + # /admin/ + path("", old_views.cdc_admin, name="cdc-admin"), + path( + "classes/", + include( + [ + # /admin/classes/ID/stats/ + path("/stats/", old_views.session_stats, name="stats"), + # /admin/classes/ID/check-in/ + path("/check-in/", old_views.session_check_in, name="student-check-in"), + # /admin/classes/ID/check-in-mentors/ + path( + "/check-in-mentors/", old_views.session_check_in_mentors, name="mentor-check-in" + ), + # /admin/classes/ID/donations/ + path("/donations/", old_views.session_donations, name="donations"), + ] + ), + ), + path( + "meetings/", + include( + [ + # /admin/meeting/ID/check-in/ + path("/check-in/", old_views.meeting_check_in, name="meeting-check-in"), + ] + ), + ), + # Admin Check System + # /admin/checksystem/ + path("checksystem/", old_views.check_system, name="check-system"), + ] + ), + ) ] # Sessions urlpatterns += [ - - path('classes/', include([ - # Classes - # /classes/ - path('', RedirectView.as_view(pattern_name='weallcode-programs'), name='sessions'), - - # Individual Class - # /classes/ID/ - path('/', SessionDetailView.as_view(), name='session-detail'), - - # Password - # /classes/ID/password/ - path('/password/', PasswordSessionView.as_view(), name='session-password'), - - # Announce - # /classes/ID/announce/mentors/ - path('/announce/mentors/', old_views.session_announce_mentors, name='session-announce-mentors'), - - # /classes/ID/announce/guardians/ - path('/announce/guardians/', old_views.session_announce_guardians, name='session-announce-guardians'), - - # Calendar - # /classes/ID/calendar/ - path('/calendar/', SessionCalendarView.as_view(), name='session-calendar'), - - # Sign up - # /classes/ID/sign-up/ - path('/sign-up/', SessionSignUpView.as_view(), name='session-sign-up'), - - # /classes/ID/sign-up/STUDENT-ID/ - path('/sign-up//', SessionSignUpView.as_view(), name='session-sign-up'), - ])), - + path( + "classes/", + include( + [ + # Classes + # /classes/ + path("", RedirectView.as_view(pattern_name="weallcode-programs"), name="sessions"), + # Individual Class + # /classes/ID/ + path("/", SessionDetailView.as_view(), name="session-detail"), + # Password + # /classes/ID/password/ + path("/password/", PasswordSessionView.as_view(), name="session-password"), + # Announce + # /classes/ID/announce/mentors/ + path("/announce/mentors/", old_views.session_announce_mentors, name="session-announce-mentors"), + # /classes/ID/announce/guardians/ + path( + "/announce/guardians/", + old_views.session_announce_guardians, + name="session-announce-guardians", + ), + # Calendar + # /classes/ID/calendar/ + path("/calendar/", SessionCalendarView.as_view(), name="session-calendar"), + # Sign up + # /classes/ID/sign-up/ + path("/sign-up/", SessionSignUpView.as_view(), name="session-sign-up"), + # /classes/ID/sign-up/STUDENT-ID/ + path("/sign-up//", SessionSignUpView.as_view(), name="session-sign-up"), + ] + ), + ), ] # Mentors # TODO: Uncomment `app_name` after we move mentors to it's own app. # app_name = 'mentors' urlpatterns += [ - path('mentors/', include([ - # Mentors - # / - path('', old_views.mentors, name='mentors'), - - # /ID/ - path('/', old_views.mentor_detail, name='mentor-detail'), - - # /ID/reject-avatar/ - path('/reject-avatar/', old_views.mentor_reject_avatar, name='mentor-reject-avatar'), - - # /ID/approve-avatar/ - path('/approve-avatar/', old_views.mentor_approve_avatar, name='mentor-approve-avatar'), - ])), - + path( + "mentors/", + include( + [ + # Mentors + # / + path("", old_views.mentors, name="mentors"), + # /ID/ + path("/", old_views.mentor_detail, name="mentor-detail"), + # /ID/reject-avatar/ + path("/reject-avatar/", old_views.mentor_reject_avatar, name="mentor-reject-avatar"), + # /ID/approve-avatar/ + path("/approve-avatar/", old_views.mentor_approve_avatar, name="mentor-approve-avatar"), + ] + ), + ), ] # Students urlpatterns += [ # Student # /student/ID/ - path('students//', old_views.student_detail, name='student-detail'), + path("students//", old_views.student_detail, name="student-detail"), ] # Dojo @@ -186,33 +185,31 @@ # Dojo / Account # /dojo/ # path('dojo/', old_views.dojo, name='dojo'), - # Welcome # /welcome/ - path('welcome/', WelcomeView.as_view(), name='welcome'), + path("welcome/", WelcomeView.as_view(), name="welcome"), ] # Meetings -urlpatterns += [ - -] +urlpatterns += [] # robots.txt urlpatterns += [ - path('robots.txt', lambda r: HttpResponse('User-agent: *\nDisallow:\nSitemap: ' + - settings.SITE_URL + '/sitemap.xml', content_type='text/plain')) + path( + "robots.txt", + lambda r: HttpResponse( + "User-agent: *\nDisallow:\nSitemap: " + settings.SITE_URL + "/sitemap.xml", content_type="text/plain" + ), + ) ] # Anymail urlpatterns += [ - path('anymail/', include('anymail.urls')), + path("anymail/", include("anymail.urls")), ] # Media -urlpatterns += static( - settings.MEDIA_URL, - document_root=settings.MEDIA_ROOT -) +urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG: from django.contrib.staticfiles.urls import staticfiles_urlpatterns @@ -222,24 +219,13 @@ # This allows the error pages to be debugged during development, just visit # these url in browser to see how these error pages look like. urlpatterns += [ - path( - '400/', - defaults.bad_request, - kwargs={'exception': Exception('Bad Request!')}, - ), - path( - '403/', - defaults.permission_denied, - kwargs={'exception': Exception('Permission Denied')}, - ), - path( - '404/', - defaults.page_not_found, - kwargs={'exception': Exception('Page not Found')}, - ), - path('500/', defaults.server_error), + path("400/", defaults.bad_request, kwargs={"exception": Exception("Bad Request!")},), + path("403/", defaults.permission_denied, kwargs={"exception": Exception("Permission Denied")},), + path("404/", defaults.page_not_found, kwargs={"exception": Exception("Page not Found")},), + path("500/", defaults.server_error), ] - if 'debug_toolbar' in settings.INSTALLED_APPS: + if "debug_toolbar" in settings.INSTALLED_APPS: import debug_toolbar - urlpatterns = [path('__debug__/', include(debug_toolbar.urls))] + urlpatterns + + urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns diff --git a/coderdojochi/util.py b/coderdojochi/util.py index 0a8ff48c..c2dbf0e8 100644 --- a/coderdojochi/util.py +++ b/coderdojochi/util.py @@ -2,11 +2,9 @@ from django.conf import settings from django.contrib.auth import get_user_model -from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string from django.utils import timezone -from anymail.exceptions import AnymailAPIError from anymail.message import AnymailMessage logger = logging.getLogger(__name__) @@ -40,12 +38,12 @@ def email( if bcc not in [False, None] and not isinstance(bcc, list): raise TypeError("recipients must be a list") - merge_global_data['subject'] = subject - merge_global_data['current_year'] = timezone.now().year - merge_global_data['company_name'] = settings.SITE_NAME - merge_global_data['site_url'] = settings.SITE_URL - merge_global_data['preheader'] = preheader - merge_global_data['unsub_group_id'] = unsub_group_id + merge_global_data["subject"] = subject + merge_global_data["current_year"] = timezone.now().year + merge_global_data["company_name"] = settings.SITE_NAME + merge_global_data["site_url"] = settings.SITE_URL + merge_global_data["preheader"] = preheader + merge_global_data["unsub_group_id"] = unsub_group_id body = render_to_string(f"{template_name}.html", merge_global_data) @@ -57,14 +55,14 @@ def email( if merge_field_format.format(key) in body: final_merge_global_data[key] = "" if val is None else str(val) - esp_extra={ - 'merge_field_format': merge_field_format, - 'categories': [template_name], + esp_extra = { + "merge_field_format": merge_field_format, + "categories": [template_name], } if unsub_group_id: - esp_extra['asm'] = { - 'group_id': unsub_group_id, + esp_extra["asm"] = { + "group_id": unsub_group_id, } for recipients_batch in batches(recipients, batch_size): @@ -96,10 +94,8 @@ def email( for recipient in msg.anymail_status.recipients.keys(): send_attempt = msg.anymail_status.recipients[recipient] - if send_attempt.status not in ['queued', 'sent']: - logger.error( - f"user: {recipient}, {timezone.now()}" - ) + if send_attempt.status not in ["queued", "sent"]: + logger.error(f"user: {recipient}, {timezone.now()}") user = User.objects.get(email=recipient) user.is_active = False @@ -109,4 +105,4 @@ def email( def batches(l, n): for i in range(0, len(l), n): - yield l[i:i + n] + yield l[i : i + n] diff --git a/coderdojochi/views/calendar.py b/coderdojochi/views/calendar.py index d8d6ad64..5a4f758c 100644 --- a/coderdojochi/views/calendar.py +++ b/coderdojochi/views/calendar.py @@ -28,47 +28,38 @@ def get_location(self, request, event_obj): raise NotImplementedError def get(self, request, *args, **kwargs): - event_obj = get_object_or_404( - self.event_class, - id=kwargs[self.event_kwarg] - ) + event_obj = get_object_or_404(self.event_class, id=kwargs[self.event_kwarg]) cal = Calendar() - cal['prodid'] = '-//We All Code//weallcode.org//' - cal['version'] = '2.0' - cal['calscale'] = 'GREGORIAN' + cal["prodid"] = "-//We All Code//weallcode.org//" + cal["version"] = "2.0" + cal["calscale"] = "GREGORIAN" event = Event() - event['uid'] = f"{self.event_type.upper()}{event_obj.id:04}@weallcode.org" - event['summary'] = self.get_summary(request, event_obj) - event['dtstart'] = self.get_dtstart(request, event_obj) - event['dtend'] = self.get_dtend(request, event_obj) - event['dtstamp'] = event['dtstart'][:-1] - event['location'] = vText(self.get_location(request, event_obj)) - event['url'] = f"{settings.SITE_URL}{event_obj.get_absolute_url()}" - event['description'] = self.get_description(event_obj) + event["uid"] = f"{self.event_type.upper()}{event_obj.id:04}@weallcode.org" + event["summary"] = self.get_summary(request, event_obj) + event["dtstart"] = self.get_dtstart(request, event_obj) + event["dtend"] = self.get_dtend(request, event_obj) + event["dtstamp"] = event["dtstart"][:-1] + event["location"] = vText(self.get_location(request, event_obj)) + event["url"] = f"{settings.SITE_URL}{event_obj.get_absolute_url()}" + event["description"] = self.get_description(event_obj) # A value of 5 is the normal or "MEDIUM" priority. # see: https://tools.ietf.org/html/rfc5545#section-3.8.1.9 - event['priority'] = 5 + event["priority"] = 5 cal.add_component(event) event_slug = "weallcode-{event_type}_{date}".format( event_type=self.event_type.lower(), - date=arrow.get( - event_obj.start_date - ).to('local').format('MM-DD-YYYY_HH-mma') + date=arrow.get(event_obj.start_date).to("local").format("MM-DD-YYYY_HH-mma"), ) # Return the ICS formatted calendar - response = HttpResponse( - cal.to_ical(), - content_type='text/calendar', - charset='utf-8' - ) + response = HttpResponse(cal.to_ical(), content_type="text/calendar", charset="utf-8") - response['Content-Disposition'] = f"attachment;filename={event_slug}.ics" + response["Content-Disposition"] = f"attachment;filename={event_slug}.ics" return response diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index e9ce8f4b..c26cac01 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -1,55 +1,17 @@ -import calendar import logging -import operator -from collections import Counter -from datetime import date, timedelta from django.conf import settings from django.contrib import messages from django.contrib.auth import get_user_model -from django.contrib.auth.decorators import login_required -from django.core.exceptions import ObjectDoesNotExist -from django.core.mail import EmailMultiAlternatives, get_connection -from django.db.models import Case, Count, IntegerField, When -from django.http import Http404, HttpResponse +from django.http import Http404 from django.shortcuts import get_object_or_404, redirect, render -from django.urls import reverse from django.utils import timezone -from django.utils.decorators import method_decorator -from django.utils.functional import cached_property from django.utils.html import strip_tags -from django.views.decorators.cache import never_cache -from django.views.decorators.csrf import csrf_exempt -from django.views.generic import DetailView, ListView, TemplateView, View +from django.views.generic import DetailView, ListView import arrow -from icalendar import Calendar, Event, vText - -from coderdojochi.forms import ( - CDCForm, - CDCModelForm, - ContactForm, - DonationForm, - GuardianForm, - MentorForm, - SignupForm, - StudentForm, -) -from coderdojochi.mixins import RoleRedirectMixin -from coderdojochi.models import ( - Donation, - Equipment, - EquipmentType, - Guardian, - Meeting, - MeetingOrder, - Mentor, - MentorOrder, - Order, - PartnerPasswordAccess, - Session, - Student, -) + +from coderdojochi.models import Meeting, MeetingOrder, Mentor from coderdojochi.util import email from coderdojochi.views.calendar import CalendarView @@ -64,9 +26,7 @@ class MeetingsView(ListView): template_name = "meetings.html" def get_queryset(self): - objects = self.model.objects.filter( - end_date__gte=timezone.now() - ) + objects = self.model.objects.filter(end_date__gte=timezone.now()) if not self.request.user.is_authenticated: objects = objects.filter(is_public=True) @@ -96,7 +56,7 @@ def get(self, request, *args, **kwargs): try: self.object = self.get_object() except Http404: - return redirect('meetings') + return redirect("meetings") context = self.get_context_data(object=self.object) return self.render_to_response(context) @@ -106,36 +66,31 @@ def get_context_data(self, **kwargs): user = self.request.user - if user.is_authenticated and user.role == 'mentor': + if user.is_authenticated and user.role == "mentor": mentor = get_object_or_404(Mentor, user=self.request.user) - active_meeting_orders = MeetingOrder.objects.filter( - meeting=self.object, - is_active=True - ) - context['active_meeting_orders'] = active_meeting_orders - context['mentor_signed_up'] = active_meeting_orders.filter( - mentor=mentor - ).exists() + active_meeting_orders = MeetingOrder.objects.filter(meeting=self.object, is_active=True) + context["active_meeting_orders"] = active_meeting_orders + context["mentor_signed_up"] = active_meeting_orders.filter(mentor=mentor).exists() return context class MeetingCalendarView(CalendarView): - event_type = 'meeting' - event_kwarg = 'pk' + event_type = "meeting" + event_kwarg = "pk" event_class = Meeting def get_summary(self, request, event_obj): - event_name = f"{event_obj.meeting_type.code} - " if event_obj.meeting_type.code else '' + event_name = f"{event_obj.meeting_type.code} - " if event_obj.meeting_type.code else "" event_name += event_obj.meeting_type.title return f"We All Code: {event_name}" def get_dtstart(self, request, event_obj): - return arrow.get(event_obj.start_date).format('YYYYMMDDTHHmmss') + return arrow.get(event_obj.start_date).format("YYYYMMDDTHHmmss") def get_dtend(self, request, event_obj): - return arrow.get(event_obj.end_date).format('YYYYMMDDTHHmmss') + return arrow.get(event_obj.end_date).format("YYYYMMDDTHHmmss") def get_description(self, event_obj): return strip_tags(event_obj.meeting_type.description) @@ -147,83 +102,61 @@ def get_location(self, request, event_obj): def meeting_sign_up(request, pk, template_name="meeting-sign-up.html"): meeting_obj = get_object_or_404(Meeting, pk=pk) - mentor = get_object_or_404( - Mentor, - user=request.user - ) + mentor = get_object_or_404(Mentor, user=request.user) - meeting_orders = MeetingOrder.objects.filter( - meeting=meeting_obj, - is_active=True - ) + meeting_orders = MeetingOrder.objects.filter(meeting=meeting_obj, is_active=True) user_meeting_order = meeting_orders.filter(mentor=mentor) user_signed_up = True if user_meeting_order.count() else False - if request.method == 'POST': + if request.method == "POST": if user_signed_up: - meeting_order = get_object_or_404( - MeetingOrder, - meeting=meeting_obj, - mentor=mentor - ) + meeting_order = get_object_or_404(MeetingOrder, meeting=meeting_obj, mentor=mentor) meeting_order.is_active = False meeting_order.save() - messages.success( - request, - 'Thanks for letting us know!' - ) + messages.success(request, "Thanks for letting us know!") else: if not settings.DEBUG: - ip = ( - request.META['HTTP_X_FORWARDED_FOR'] or - request.META['REMOTE_ADDR'] - ) + ip = request.META["HTTP_X_FORWARDED_FOR"] or request.META["REMOTE_ADDR"] else: - ip = request.META['REMOTE_ADDR'] + ip = request.META["REMOTE_ADDR"] - meeting_order, created = MeetingOrder.objects.get_or_create( - mentor=mentor, - meeting=meeting_obj - ) + meeting_order, created = MeetingOrder.objects.get_or_create(mentor=mentor, meeting=meeting_obj) meeting_order.ip = ip meeting_order.is_active = True meeting_order.save() - messages.success( - request, - 'Success! See you there!' - ) + messages.success(request, "Success! See you there!") merge_global_data = { - 'first_name': request.user.first_name, - 'last_name': request.user.last_name, - 'order_id': meeting_order.id, - 'meeting_title': meeting_obj.meeting_type.title, - 'meeting_description': meeting_obj.meeting_type.description, - 'meeting_start_date': arrow.get(meeting_obj.start_date).to('local').format('dddd, MMMM D, YYYY'), - 'meeting_start_time': arrow.get(meeting_obj.start_date).to('local').format('h:mma'), - 'meeting_end_date': arrow.get(meeting_obj.end_date).to('local').format('dddd, MMMM D, YYYY'), - 'meeting_end_time': arrow.get(meeting_obj.end_date).to('local').format('h:mma'), - 'meeting_location_name': meeting_obj.location.name, - 'meeting_location_address': meeting_obj.location.address, - 'meeting_location_city': meeting_obj.location.city, - 'meeting_location_state': meeting_obj.location.state, - 'meeting_location_zip': meeting_obj.location.zip, - 'meeting_additional_info': meeting_obj.additional_info, - 'meeting_url': f"{settings.SITE_URL}{meeting_obj.get_absolute_url()}", - 'meeting_calendar_url': f"{settings.SITE_URL}{meeting_obj.get_calendar_url()}", - 'microdata_start_date': arrow.get(meeting_obj.start_date).to('local').isoformat(), - 'microdata_end_date': arrow.get(meeting_obj.end_date).to('local').isoformat(), + "first_name": request.user.first_name, + "last_name": request.user.last_name, + "order_id": meeting_order.id, + "meeting_title": meeting_obj.meeting_type.title, + "meeting_description": meeting_obj.meeting_type.description, + "meeting_start_date": arrow.get(meeting_obj.start_date).to("local").format("dddd, MMMM D, YYYY"), + "meeting_start_time": arrow.get(meeting_obj.start_date).to("local").format("h:mma"), + "meeting_end_date": arrow.get(meeting_obj.end_date).to("local").format("dddd, MMMM D, YYYY"), + "meeting_end_time": arrow.get(meeting_obj.end_date).to("local").format("h:mma"), + "meeting_location_name": meeting_obj.location.name, + "meeting_location_address": meeting_obj.location.address, + "meeting_location_city": meeting_obj.location.city, + "meeting_location_state": meeting_obj.location.state, + "meeting_location_zip": meeting_obj.location.zip, + "meeting_additional_info": meeting_obj.additional_info, + "meeting_url": f"{settings.SITE_URL}{meeting_obj.get_absolute_url()}", + "meeting_calendar_url": f"{settings.SITE_URL}{meeting_obj.get_calendar_url()}", + "microdata_start_date": arrow.get(meeting_obj.start_date).to("local").isoformat(), + "microdata_end_date": arrow.get(meeting_obj.end_date).to("local").isoformat(), } email( - subject='Upcoming mentor meeting confirmation', - template_name='meeting-confirm-mentor', + subject="Upcoming mentor meeting confirmation", + template_name="meeting-confirm-mentor", merge_global_data=merge_global_data, recipients=[request.user.email], preheader=( @@ -232,76 +165,61 @@ def meeting_sign_up(request, pk, template_name="meeting-sign-up.html"): ), ) - return redirect('meeting-detail', meeting_obj.id) + return redirect("meeting-detail", meeting_obj.id) - return render(request, template_name, { - 'meeting': meeting_obj, - 'user_signed_up': user_signed_up - }) + return render(request, template_name, {"meeting": meeting_obj, "user_signed_up": user_signed_up}) def meeting_announce(request, pk): if not request.user.is_staff: - messages.error( - request, - 'You do not have permission to access this page.' - ) - return redirect('home') + messages.error(request, "You do not have permission to access this page.") + return redirect("home") meeting_obj = get_object_or_404(Meeting, pk=pk) if not meeting_obj.announced_date: merge_data = {} merge_global_data = { - 'meeting_title': meeting_obj.meeting_type.title, - 'meeting_description': meeting_obj.meeting_type.description, - 'meeting_start_date': arrow.get(meeting_obj.start_date).to('local').format('dddd, MMMM D, YYYY'), - 'meeting_start_time': arrow.get(meeting_obj.start_date).to('local').format('h:mma'), - 'meeting_end_date': arrow.get(meeting_obj.end_date).to('local').format('dddd, MMMM D, YYYY'), - 'meeting_end_time': arrow.get(meeting_obj.end_date).to('local').format('h:mma'), - 'meeting_location_name': meeting_obj.location.name, - 'meeting_location_address': meeting_obj.location.address, - 'meeting_location_city': meeting_obj.location.city, - 'meeting_location_state': meeting_obj.location.state, - 'meeting_location_zip': meeting_obj.location.zip, - 'meeting_additional_info': meeting_obj.additional_info, - 'meeting_url': f"{settings.SITE_URL}{meeting_obj.get_absolute_url()}", - 'meeting_calendar_url': f"{settings.SITE_URL}{meeting_obj.get_calendar_url()}", + "meeting_title": meeting_obj.meeting_type.title, + "meeting_description": meeting_obj.meeting_type.description, + "meeting_start_date": arrow.get(meeting_obj.start_date).to("local").format("dddd, MMMM D, YYYY"), + "meeting_start_time": arrow.get(meeting_obj.start_date).to("local").format("h:mma"), + "meeting_end_date": arrow.get(meeting_obj.end_date).to("local").format("dddd, MMMM D, YYYY"), + "meeting_end_time": arrow.get(meeting_obj.end_date).to("local").format("h:mma"), + "meeting_location_name": meeting_obj.location.name, + "meeting_location_address": meeting_obj.location.address, + "meeting_location_city": meeting_obj.location.city, + "meeting_location_state": meeting_obj.location.state, + "meeting_location_zip": meeting_obj.location.zip, + "meeting_additional_info": meeting_obj.additional_info, + "meeting_url": f"{settings.SITE_URL}{meeting_obj.get_absolute_url()}", + "meeting_calendar_url": f"{settings.SITE_URL}{meeting_obj.get_calendar_url()}", } - mentors = Mentor.objects.filter( - is_active=True, - user__is_active=True, - ) + mentors = Mentor.objects.filter(is_active=True, user__is_active=True,) recipients = [] for mentor in mentors: recipients.append(mentor.user.email) merge_data[mentor.user.email] = { - 'first_name': mentor.user.first_name, - 'last_name': mentor.user.last_name, + "first_name": mentor.user.first_name, + "last_name": mentor.user.last_name, } email( - subject='New meeting announced!', - template_name='meeting-announcement-mentor', + subject="New meeting announced!", + template_name="meeting-announcement-mentor", merge_data=merge_data, merge_global_data=merge_global_data, recipients=recipients, - preheader='A new meeting has been announced. Come join us for some amazing fun!', + preheader="A new meeting has been announced. Come join us for some amazing fun!", ) meeting_obj.announced_date = timezone.now() meeting_obj.save() - messages.success( - request, - f"Meeting announced to {mentors.count()} mentors." - ) + messages.success(request, f"Meeting announced to {mentors.count()} mentors.") else: - messages.warning( - request, - 'Meeting already announced.' - ) + messages.warning(request, "Meeting already announced.") - return redirect('cdc-admin') + return redirect("cdc-admin") diff --git a/coderdojochi/views/profile.py b/coderdojochi/views/profile.py index ce48f61f..aea39b40 100644 --- a/coderdojochi/views/profile.py +++ b/coderdojochi/views/profile.py @@ -1,52 +1,15 @@ -import calendar import logging -import operator -from collections import Counter -from datetime import date, timedelta -from django.conf import settings from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required -from django.core.exceptions import ObjectDoesNotExist -from django.core.mail import EmailMultiAlternatives, get_connection -from django.db.models import Case, Count, IntegerField, When -from django.http import HttpResponse -from django.shortcuts import get_object_or_404, redirect, render -from django.urls import reverse +from django.shortcuts import get_object_or_404, redirect from django.utils import timezone from django.utils.decorators import method_decorator -from django.utils.html import strip_tags -from django.views.decorators.cache import never_cache -from django.views.decorators.csrf import csrf_exempt from django.views.generic import TemplateView -import arrow -from icalendar import Calendar, Event, vText - -from coderdojochi.forms import ( - CDCModelForm, - ContactForm, - DonationForm, - GuardianForm, - MentorForm, - StudentForm, -) -from coderdojochi.models import ( - Donation, - Equipment, - EquipmentType, - Guardian, - Meeting, - MeetingOrder, - Mentor, - MentorOrder, - Order, - PartnerPasswordAccess, - Session, - Student, -) -from coderdojochi.util import email +from coderdojochi.forms import CDCModelForm, MentorForm +from coderdojochi.models import Mentor, MentorOrder logger = logging.getLogger(__name__) @@ -55,7 +18,7 @@ class DojoMentorView(TemplateView): - template_name = 'mentor/dojo.html' + template_name = "mentor/dojo.html" @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): @@ -63,77 +26,51 @@ def dispatch(self, request, *args, **kwargs): def get_context_data(self, **kwargs): context = super(DojoMentorView, self).get_context_data(**kwargs) - context['highlight'] = self.request.GET.get('highlight', False) + context["highlight"] = self.request.GET.get("highlight", False) mentor = get_object_or_404(Mentor, user=self.request.user) - context['mentor'] = mentor + context["mentor"] = mentor - orders = MentorOrder.objects.select_related().filter( - is_active=True, - mentor=context['mentor'], - ) - - upcoming_sessions = orders.filter( - is_active=True, - session__start_date__gte=timezone.now() - ).order_by('session__start_date') + orders = MentorOrder.objects.select_related().filter(is_active=True, mentor=context["mentor"],) - past_sessions = orders.filter( - is_active=True, - session__start_date__lte=timezone.now() - ).order_by('session__start_date') + # upcoming_sessions = orders.filter(is_active=True, session__start_date__gte=timezone.now()).order_by( + # "session__start_date" + # ) - meeting_orders = MeetingOrder.objects.select_related().filter( - mentor=mentor + past_sessions = orders.filter(is_active=True, session__start_date__lte=timezone.now()).order_by( + "session__start_date" ) - upcoming_meetings = meeting_orders.filter( - is_active=True, - meeting__is_public=True, - meeting__end_date__gte=timezone.now() - ).order_by('meeting__start_date') + # meeting_orders = MeetingOrder.objects.select_related().filter(mentor=mentor) - context['account_complete'] = False + # upcoming_meetings = meeting_orders.filter( + # is_active=True, meeting__is_public=True, meeting__end_date__gte=timezone.now() + # ).order_by("meeting__start_date") + + context["account_complete"] = False if ( - mentor.user.first_name and - mentor.user.last_name and - mentor.avatar and - mentor.background_check and - past_sessions.count() > 0 + mentor.user.first_name + and mentor.user.last_name + and mentor.avatar + and mentor.background_check + and past_sessions.count() > 0 ): - context['account_complete'] = True + context["account_complete"] = True return context def post(self, request, *args, **kwargs): mentor = get_object_or_404(Mentor, user=request.user) - form = MentorForm( - request.POST, - request.FILES, - instance=mentor - ) + form = MentorForm(request.POST, request.FILES, instance=mentor) - user_form = CDCModelForm( - request.POST, - request.FILES, - instance=mentor.user - ) + user_form = CDCModelForm(request.POST, request.FILES, instance=mentor.user) - if ( - form.is_valid() and - user_form.is_valid() - ): + if form.is_valid() and user_form.is_valid(): form.save() user_form.save() - messages.success( - request, - 'Profile information saved.' - ) + messages.success(request, "Profile information saved.") - return redirect('account_home') + return redirect("account_home") else: - messages.error( - request, - 'There was an error. Please try again.' - ) + messages.error(request, "There was an error. Please try again.") diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index a8164609..e2c2e672 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -1,5 +1,4 @@ import logging -from datetime import date from django.conf import settings from django.contrib import messages @@ -13,18 +12,9 @@ from django.views.generic import TemplateView import arrow -from dateutil.relativedelta import relativedelta from coderdojochi.mixins import RoleRedirectMixin, RoleTemplateMixin -from coderdojochi.models import ( - Guardian, - Mentor, - MentorOrder, - Order, - PartnerPasswordAccess, - Session, - Student, -) +from coderdojochi.models import Guardian, Mentor, MentorOrder, Order, PartnerPasswordAccess, Session, Student from coderdojochi.util import email from coderdojochi.views.calendar import CalendarView @@ -36,35 +26,35 @@ def session_confirm_mentor(request, session_obj, order): merge_global_data = { - 'first_name': request.user.first_name, - 'last_name': request.user.last_name, - 'class_code': session_obj.course.code, - 'class_title': session_obj.course.title, - 'class_description': session_obj.course.description, - 'class_start_date': arrow.get(session_obj.mentor_start_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_start_time': arrow.get(session_obj.mentor_start_date).to('local').format('h:mma'), - 'class_end_date': arrow.get(session_obj.mentor_end_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_end_time': arrow.get(session_obj.mentor_end_date).to('local').format('h:mma'), - 'class_location_name': session_obj.location.name, - 'class_location_address': session_obj.location.address, - 'class_location_city': session_obj.location.city, - 'class_location_state': session_obj.location.state, - 'class_location_zip': session_obj.location.zip, - 'class_additional_info': session_obj.additional_info, - 'class_url': f"{settings.SITE_URL}{session_obj.get_absolute_url()}", - 'class_calendar_url': f"{settings.SITE_URL}{session_obj.get_calendar_url()}", - 'microdata_start_date': arrow.get(session_obj.mentor_start_date).to('local').isoformat(), - 'microdata_end_date': arrow.get(session_obj.mentor_end_date).to('local').isoformat(), - 'order_id': order.id, - 'online_video_link': session_obj.online_video_link, - 'online_video_description': session_obj.online_video_description, + "first_name": request.user.first_name, + "last_name": request.user.last_name, + "class_code": session_obj.course.code, + "class_title": session_obj.course.title, + "class_description": session_obj.course.description, + "class_start_date": arrow.get(session_obj.mentor_start_date).to("local").format("dddd, MMMM D, YYYY"), + "class_start_time": arrow.get(session_obj.mentor_start_date).to("local").format("h:mma"), + "class_end_date": arrow.get(session_obj.mentor_end_date).to("local").format("dddd, MMMM D, YYYY"), + "class_end_time": arrow.get(session_obj.mentor_end_date).to("local").format("h:mma"), + "class_location_name": session_obj.location.name, + "class_location_address": session_obj.location.address, + "class_location_city": session_obj.location.city, + "class_location_state": session_obj.location.state, + "class_location_zip": session_obj.location.zip, + "class_additional_info": session_obj.additional_info, + "class_url": f"{settings.SITE_URL}{session_obj.get_absolute_url()}", + "class_calendar_url": f"{settings.SITE_URL}{session_obj.get_calendar_url()}", + "microdata_start_date": arrow.get(session_obj.mentor_start_date).to("local").isoformat(), + "microdata_end_date": arrow.get(session_obj.mentor_end_date).to("local").isoformat(), + "order_id": order.id, + "online_video_link": session_obj.online_video_link, + "online_video_description": session_obj.online_video_description, } email( - subject='Mentoring confirmation for {} class'.format( - arrow.get(session_obj.mentor_start_date).to('local').format('MMMM D'), + subject="Mentoring confirmation for {} class".format( + arrow.get(session_obj.mentor_start_date).to("local").format("MMMM D"), ), - template_name='class-confirm-mentor', + template_name="class-confirm-mentor", merge_global_data=merge_global_data, recipients=[request.user.email], preheader="It's time to use your powers for good.", @@ -73,38 +63,38 @@ def session_confirm_mentor(request, session_obj, order): def session_confirm_guardian(request, session_obj, order, student): merge_global_data = { - 'first_name': request.user.first_name, - 'last_name': request.user.last_name, - 'student_first_name': student.first_name, - 'student_last_name': student.last_name, - 'class_code': session_obj.course.code, - 'class_title': session_obj.course.title, - 'class_description': session_obj.course.description, - 'class_start_date': arrow.get(session_obj.start_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_start_time': arrow.get(session_obj.start_date).to('local').format('h:mma'), - 'class_end_date': arrow.get(session_obj.end_date).to('local').format('dddd, MMMM D, YYYY'), - 'class_end_time': arrow.get(session_obj.end_date).to('local').format('h:mma'), - 'class_location_name': session_obj.location.name, - 'class_location_address': session_obj.location.address, - 'class_location_city': session_obj.location.city, - 'class_location_state': session_obj.location.state, - 'class_location_zip': session_obj.location.zip, - 'class_additional_info': session_obj.additional_info, - 'class_url': session_obj.get_absolute_url(), - 'class_calendar_url': session_obj.get_calendar_url(), - 'microdata_start_date': arrow.get(session_obj.start_date).to('local').isoformat(), - 'microdata_end_date': arrow.get(session_obj.end_date).to('local').isoformat(), - 'order_id': order.id, - 'online_video_link': session_obj.online_video_link, - 'online_video_description': session_obj.online_video_description, + "first_name": request.user.first_name, + "last_name": request.user.last_name, + "student_first_name": student.first_name, + "student_last_name": student.last_name, + "class_code": session_obj.course.code, + "class_title": session_obj.course.title, + "class_description": session_obj.course.description, + "class_start_date": arrow.get(session_obj.start_date).to("local").format("dddd, MMMM D, YYYY"), + "class_start_time": arrow.get(session_obj.start_date).to("local").format("h:mma"), + "class_end_date": arrow.get(session_obj.end_date).to("local").format("dddd, MMMM D, YYYY"), + "class_end_time": arrow.get(session_obj.end_date).to("local").format("h:mma"), + "class_location_name": session_obj.location.name, + "class_location_address": session_obj.location.address, + "class_location_city": session_obj.location.city, + "class_location_state": session_obj.location.state, + "class_location_zip": session_obj.location.zip, + "class_additional_info": session_obj.additional_info, + "class_url": session_obj.get_absolute_url(), + "class_calendar_url": session_obj.get_calendar_url(), + "microdata_start_date": arrow.get(session_obj.start_date).to("local").isoformat(), + "microdata_end_date": arrow.get(session_obj.end_date).to("local").isoformat(), + "order_id": order.id, + "online_video_link": session_obj.online_video_link, + "online_video_description": session_obj.online_video_description, } email( - subject=f'Upcoming class confirmation for {student.first_name} {student.last_name}', - template_name='class-confirm-guardian', + subject=f"Upcoming class confirmation for {student.first_name} {student.last_name}", + template_name="class-confirm-guardian", merge_global_data=merge_global_data, recipients=[request.user.email], - preheader='Magical wizards have generated this confirmation. All thanks to the mystical power of coding.', + preheader="Magical wizards have generated this confirmation. All thanks to the mystical power of coding.", ) @@ -112,48 +102,42 @@ class SessionDetailView(RoleRedirectMixin, RoleTemplateMixin, TemplateView): template_name = "session-detail.html" def dispatch(self, request, *args, **kwargs): - session_obj = get_object_or_404(Session, id=kwargs['pk']) + session_obj = get_object_or_404(Session, id=kwargs["pk"]) - if request.method == 'GET': - if session_obj.password and not self.validate_partner_session_access(self.request, kwargs['pk']): - return redirect(reverse('session-password', kwargs=kwargs)) + if request.method == "GET": + if session_obj.password and not self.validate_partner_session_access(self.request, kwargs["pk"]): + return redirect(reverse("session-password", kwargs=kwargs)) if request.user.is_authenticated and request.user.role: - if 'enroll' in request.GET or 'enroll' in kwargs: + if "enroll" in request.GET or "enroll" in kwargs: return self.enroll_redirect(request, session_obj) - kwargs['session_obj'] = session_obj + kwargs["session_obj"] = session_obj return super(SessionDetailView, self).dispatch(request, *args, **kwargs) def enroll_redirect(self, request, session_obj): - if request.user.role == 'mentor': - return redirect('session-sign-up', pk=session_obj.id) + if request.user.role == "mentor": + return redirect("session-sign-up", pk=session_obj.id) guardian = get_object_or_404(Guardian, user=request.user) - student = get_object_or_404(Student, guardian=guardian, id=(int(request.GET['student']))) + student = get_object_or_404(Student, guardian=guardian, id=(int(request.GET["student"]))) if student: - return redirect('session-sign-up', pk=session_obj.id, student_id=student.id) + return redirect("session-sign-up", pk=session_obj.id, student_id=student.id) return redirect(f"{reverse('welcome')}?next={session_obj.get_absolute_url()}&enroll=True") def validate_partner_session_access(self, request, pk): - authed_sessions = request.session.get('authed_partner_sessions') + authed_sessions = request.session.get("authed_partner_sessions") if authed_sessions and pk in authed_sessions: if request.user.is_authenticated: - PartnerPasswordAccess.objects.get_or_create( - session_id=pk, - user=request.user - ) + PartnerPasswordAccess.objects.get_or_create(session_id=pk, user=request.user) return True if request.user.is_authenticated: try: - PartnerPasswordAccess.objects.get( - session_id=pk, - user_id=request.user.id - ) + PartnerPasswordAccess.objects.get(session_id=pk, user_id=request.user.id) except PartnerPasswordAccess.DoesNotExist: return False else: @@ -164,81 +148,56 @@ def validate_partner_session_access(self, request, pk): def get_context_data(self, **kwargs): context = super(SessionDetailView, self).get_context_data(**kwargs) - session_obj = kwargs['session_obj'] - context['session'] = session_obj + session_obj = kwargs["session_obj"] + context["session"] = session_obj - upcoming_classes = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now() - ).order_by('start_date') - context['upcoming_classes'] = upcoming_classes + upcoming_classes = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") + context["upcoming_classes"] = upcoming_classes active_mentors = Mentor.objects.filter( - id__in=MentorOrder.objects.filter( - session=session_obj, - is_active=True - ).values('mentor__id') + id__in=MentorOrder.objects.filter(session=session_obj, is_active=True).values("mentor__id") ) - context['active_mentors'] = active_mentors + context["active_mentors"] = active_mentors if self.request.user.is_authenticated: - if self.request.user.role == 'mentor': + if self.request.user.role == "mentor": account = get_object_or_404(Mentor, user=self.request.user) - session_orders = MentorOrder.objects.filter( - session=session_obj, - is_active=True, - ) - context['mentor_signed_up'] = session_orders.filter( - mentor=account - ).exists() - - context['spots_remaining'] = ( - session_obj.get_mentor_capacity() - session_orders.count() - ) + session_orders = MentorOrder.objects.filter(session=session_obj, is_active=True,) + context["mentor_signed_up"] = session_orders.filter(mentor=account).exists() + + context["spots_remaining"] = session_obj.get_mentor_capacity() - session_orders.count() else: account = get_object_or_404(Guardian, user=self.request.user) - context['students'] = account.get_students() - context['spots_remaining'] = ( - session_obj.capacity - - session_obj.get_current_students().count() - ) - context['account'] = account + context["students"] = account.get_students() + context["spots_remaining"] = session_obj.capacity - session_obj.get_current_students().count() + context["account"] = account else: - context['upcoming_classes'] = upcoming_classes.filter(is_public=True) - context['spots_remaining'] = ( - session_obj.capacity - - session_obj.get_current_students().count() - ) + context["upcoming_classes"] = upcoming_classes.filter(is_public=True) + context["spots_remaining"] = session_obj.capacity - session_obj.get_current_students().count() return context def post(self, request, *args, **kwargs): - session_obj = kwargs['session_obj'] - if 'waitlist' not in request.POST: - messages.error(request, 'Invalid request, please try again.') + session_obj = kwargs["session_obj"] + if "waitlist" not in request.POST: + messages.error(request, "Invalid request, please try again.") return redirect(session_obj.get_absolute_url()) - if request.POST['waitlist'] == 'student': - account = Student.objects.get(id=request.POST['account_id']) - waitlist_attr = 'waitlist_students' + if request.POST["waitlist"] == "student": + account = Student.objects.get(id=request.POST["account_id"]) + waitlist_attr = "waitlist_students" else: - account = Guardian.objects.get(id=request.POST['account_id']) - waitlist_attr = 'waitlist_guardians' + account = Guardian.objects.get(id=request.POST["account_id"]) + waitlist_attr = "waitlist_guardians" - if request.POST['remove'] == 'true': + if request.POST["remove"] == "true": getattr(session_obj, waitlist_attr).remove(account) session_obj.save() - messages.success( - request, - 'You have been removed from the waitlist. Thanks for letting us know.' - ) + messages.success(request, "You have been removed from the waitlist. Thanks for letting us know.") else: getattr(session_obj, waitlist_attr).add(account) session_obj.save() - messages.success( - request, - 'Added to waitlist successfully.' - ) + messages.success(request, "Added to waitlist successfully.") return redirect(session_obj.get_absolute_url()) @@ -248,66 +207,56 @@ class SessionSignUpView(RoleRedirectMixin, RoleTemplateMixin, TemplateView): @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): - session_obj = get_object_or_404(Session, id=kwargs['pk']) - kwargs['session_obj'] = session_obj + session_obj = get_object_or_404(Session, id=kwargs["pk"]) + kwargs["session_obj"] = session_obj - if request.user.role == 'mentor': - session_orders = MentorOrder.objects.filter( - session=session_obj, - is_active=True - ) - kwargs['mentor'] = get_object_or_404(Mentor, user=request.user) - kwargs['user_signed_up'] = session_orders.filter( - mentor=kwargs['mentor'] - ).exists() + if request.user.role == "mentor": + session_orders = MentorOrder.objects.filter(session=session_obj, is_active=True) + kwargs["mentor"] = get_object_or_404(Mentor, user=request.user) + kwargs["user_signed_up"] = session_orders.filter(mentor=kwargs["mentor"]).exists() - elif request.user.role == 'guardian': - kwargs['guardian'] = get_object_or_404(Guardian, user=request.user) - kwargs['student'] = get_object_or_404(Student, id=kwargs['student_id']) - kwargs['user_signed_up'] = kwargs['student'].is_registered_for_session(session_obj) + elif request.user.role == "guardian": + kwargs["guardian"] = get_object_or_404(Guardian, user=request.user) + kwargs["student"] = get_object_or_404(Student, id=kwargs["student_id"]) + kwargs["user_signed_up"] = kwargs["student"].is_registered_for_session(session_obj) access_dict = self.check_access(request, *args, **kwargs) - if access_dict.get('message'): - if access_dict.get('redirect') == 'account_home': - messages.warning(request, access_dict['message']) + if access_dict.get("message"): + if access_dict.get("redirect") == "account_home": + messages.warning(request, access_dict["message"]) else: - messages.error(request, access_dict['message']) + messages.error(request, access_dict["message"]) - return redirect(access_dict['redirect']) + return redirect(access_dict["redirect"]) return super(SessionSignUpView, self).dispatch(request, *args, **kwargs) def check_access(self, request, *args, **kwargs): - bg_check_link = "https://app.sterlingvolunteers.com/promoorder/3df76c55-9961-46e1-8e5f-f6b38e2ec4dc" + BG_CHECK_LINK = "https://app.sterlingvolunteers.com/promoorder/3df76c55-9961-46e1-8e5f-f6b38e2ec4dc" access_dict = {} # Returns a message and redirect url if not working as dict - if kwargs.get('mentor'): - if not kwargs['mentor'].background_check: + if kwargs.get("mentor"): + if not kwargs["mentor"].background_check: access_dict = { - 'message': ( + "message": ( "You cannot sign up for a class until you " - "fill out the background search form." + 'fill out the background search form.' ), - 'redirect': request.META.get('HTTP_REFERER', '/dojo') + "redirect": request.META.get("HTTP_REFERER", "/dojo"), } - if kwargs.get('student'): - limits = self.student_limitations( - kwargs['student'], kwargs['session_obj'], kwargs['user_signed_up'] - ) + if kwargs.get("student"): + limits = self.student_limitations(kwargs["student"], kwargs["session_obj"], kwargs["user_signed_up"]) if limits: - access_dict = { - 'message': limits, - 'redirect': kwargs['session_obj'].get_absolute_url() - } + access_dict = {"message": limits, "redirect": kwargs["session_obj"].get_absolute_url()} return access_dict def student_limitations(self, student, session_obj, user_signed_up): if not student.is_within_gender_limitation(session_obj.gender_limitation): - return f'Sorry, this class is limited to {session_obj.gender_limitation}s this time around.' + return f"Sorry, this class is limited to {session_obj.gender_limitation}s this time around." if not student.is_within_age_range(session_obj.minimum_age, session_obj.maximum_age): return ( @@ -322,59 +271,43 @@ def student_limitations(self, student, session_obj, user_signed_up): def get_context_data(self, **kwargs): context = super(SessionSignUpView, self).get_context_data(**kwargs) - context['session'] = kwargs['session_obj'] - context['user_signed_up'] = kwargs.get('user_signed_up') - context['student'] = kwargs.get('student') + context["session"] = kwargs["session_obj"] + context["user_signed_up"] = kwargs.get("user_signed_up") + context["student"] = kwargs.get("student") return context def post(self, request, *args, **kwargs): - session_obj = kwargs['session_obj'] - user_signed_up = kwargs['user_signed_up'] - mentor = kwargs.get('mentor') - guardian = kwargs.get('guardian') - student = kwargs.get('student') + session_obj = kwargs["session_obj"] + user_signed_up = kwargs["user_signed_up"] + mentor = kwargs.get("mentor") + guardian = kwargs.get("guardian") + student = kwargs.get("student") if user_signed_up: if mentor: - order = get_object_or_404( - MentorOrder, - mentor=mentor, - session=session_obj - ) + order = get_object_or_404(MentorOrder, mentor=mentor, session=session_obj) elif student: - order = get_object_or_404( - Order, - student=student, - session=session_obj, - is_active=True, - ) + order = get_object_or_404(Order, student=student, session=session_obj, is_active=True,) order.is_active = False order.save() - messages.success(request, 'Thanks for letting us know!') + messages.success(request, "Thanks for letting us know!") else: - ip = request.META['REMOTE_ADDR'] + ip = request.META["REMOTE_ADDR"] if not settings.DEBUG: - ip = request.META['HTTP_X_FORWARDED_FOR'] or request.META['REMOTE_ADDR'] + ip = request.META["HTTP_X_FORWARDED_FOR"] or request.META["REMOTE_ADDR"] if mentor: - order, created = MentorOrder.objects.get_or_create( - mentor=mentor, - session=session_obj, - ) + order, created = MentorOrder.objects.get_or_create(mentor=mentor, session=session_obj,) else: - order, created = Order.objects.get_or_create( - guardian=guardian, - student=student, - session=session_obj, - ) + order, created = Order.objects.get_or_create(guardian=guardian, student=student, session=session_obj,) order.ip = ip order.is_active = True order.save() - messages.success(request, 'Success! See you there!') + messages.success(request, "Success! See you there!") if mentor: session_confirm_mentor(request, session_obj, order) @@ -385,55 +318,52 @@ def post(self, request, *args, **kwargs): class PasswordSessionView(TemplateView): - template_name = 'session-partner-password.html' + template_name = "session-partner-password.html" def get_context_data(self, **kwargs): context = super(PasswordSessionView, self).get_context_data(**kwargs) - session_obj = get_object_or_404(Session, id=kwargs.get('pk')) + session_obj = get_object_or_404(Session, id=kwargs.get("pk")) - context['partner_message'] = session_obj.partner_message + context["partner_message"] = session_obj.partner_message return context def post(self, request, *args, **kwargs): - session_obj = get_object_or_404(Session, id=kwargs.get('pk')) - password_input = request.POST.get('password') + session_obj = get_object_or_404(Session, id=kwargs.get("pk")) + password_input = request.POST.get("password") context = self.get_context_data(**kwargs) if not password_input: - context['error'] = 'Must enter a password.' + context["error"] = "Must enter a password." return render(request, self.template_name, context) if session_obj.password != password_input: - context['error'] = 'Invalid password.' + context["error"] = "Invalid password." return render(request, self.template_name, context) # Get from user session or create an empty set - authed_partner_sessions = request.session.get('authed_partner_sessions', []) + authed_partner_sessions = request.session.get("authed_partner_sessions", []) # Add course session id to user session - authed_partner_sessions.append(kwargs.get('pk')) + authed_partner_sessions.append(kwargs.get("pk")) # Remove duplicates authed_partner_sessions = list(set(authed_partner_sessions)) # Store it. - request.session['authed_partner_sessions'] = authed_partner_sessions + request.session["authed_partner_sessions"] = authed_partner_sessions if request.user.is_authenticated: - PartnerPasswordAccess.objects.get_or_create( - session=session_obj, - user=request.user - ) + PartnerPasswordAccess.objects.get_or_create(session=session_obj, user=request.user) return redirect(session_obj) class SessionCalendarView(CalendarView): - event_type = 'class' - event_kwarg = 'pk' + event_type = "class" + event_kwarg = "pk" event_class = Session def get_summary(self, request, event_obj): @@ -442,14 +372,14 @@ def get_summary(self, request, event_obj): def get_dtstart(self, request, event_obj): dtstart = f"{arrow.get(event_obj.start_date).format('YYYYMMDDTHHmmss')}Z" - if request.user.is_authenticated and request.user.role == 'mentor': + if request.user.is_authenticated and request.user.role == "mentor": dtstart = f"{arrow.get(event_obj.mentor_start_date).format('YYYYMMDDTHHmmss')}Z" return dtstart def get_dtend(self, request, event_obj): dtend = f"{arrow.get(event_obj.end_date).format('YYYYMMDDTHHmmss')}Z" - if request.user.is_authenticated and request.user.role == 'mentor': + if request.user.is_authenticated and request.user.role == "mentor": dtend = f"{arrow.get(event_obj.mentor_end_date).format('YYYYMMDDTHHmmss')}Z" return dtend @@ -463,13 +393,11 @@ def get_location(self, request, event_obj): # If user has a ticket with us, show online link if event_obj.online_video_link and self.request.user.is_authenticated: - if self.request.user.role == 'mentor': + if self.request.user.role == "mentor": try: mentor = Mentor.objects.get(user=self.request.user) mentor_signed_up = MentorOrder.objects.filter( - session=event_obj, - is_active=True, - mentor=mentor + session=event_obj, is_active=True, mentor=mentor ).exists() if mentor_signed_up: @@ -478,14 +406,12 @@ def get_location(self, request, event_obj): except Mentor.DoesNotExist: pass - elif self.request.user.role == 'guardian': + elif self.request.user.role == "guardian": try: guardian = Guardian.objects.get(user=self.request.user) students = guardian.get_students() students_signed_up = Order.objects.filter( - session=event_obj, - is_active=True, - student__in=students, + session=event_obj, is_active=True, student__in=students, ).exists() if students_signed_up: diff --git a/coderdojochi/views/welcome.py b/coderdojochi/views/welcome.py index 01d09843..b02a2b1a 100644 --- a/coderdojochi/views/welcome.py +++ b/coderdojochi/views/welcome.py @@ -9,14 +9,7 @@ from django.views.generic import TemplateView from coderdojochi.forms import GuardianForm, MentorForm, StudentForm -from coderdojochi.models import ( - Guardian, - Meeting, - MeetingOrder, - Mentor, - MentorOrder, - Session, -) +from coderdojochi.models import Guardian, Meeting, Mentor, Session from coderdojochi.util import email logger = logging.getLogger(__name__) @@ -27,55 +20,52 @@ class WelcomeView(TemplateView): @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): - next_url = request.GET.get('next') - kwargs['next_url'] = next_url + next_url = request.GET.get("next") + kwargs["next_url"] = next_url # Check for redirect condition on mentor, otherwise pass as kwarg - if ( - getattr(request.user, 'role', False) == 'mentor' - and request.method == 'GET' - ): + if getattr(request.user, "role", False) == "mentor" and request.method == "GET": mentor = get_object_or_404(Mentor, user=request.user) if mentor.user.first_name: - return redirect(next_url if next_url else 'account_home') - kwargs['mentor'] = mentor + return redirect(next_url if next_url else "account_home") + kwargs["mentor"] = mentor return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) user = self.request.user - mentor = kwargs.get('mentor') + mentor = kwargs.get("mentor") account = False - role = getattr(user, 'role', False) + role = getattr(user, "role", False) - context['role'] = role - context['next_url'] = kwargs['next_url'] + context["role"] = role + context["next_url"] = kwargs["next_url"] if mentor: account = mentor - context['form'] = MentorForm(instance=account) - if role == 'guardian': + context["form"] = MentorForm(instance=account) + if role == "guardian": guardian = get_object_or_404(Guardian, user=user) account = guardian if not account.phone or not account.zip: - context['form'] = GuardianForm(instance=account) + context["form"] = GuardianForm(instance=account) else: - context['add_student'] = True - context['form'] = StudentForm(initial={'guardian': guardian.pk}) + context["add_student"] = True + context["form"] = StudentForm(initial={"guardian": guardian.pk}) if account.user.first_name and account.get_students(): - context['students'] = account.get_students().count() + context["students"] = account.get_students().count() - context['account'] = account + context["account"] = account return context def post(self, request, *args, **kwargs): user = request.user - role = getattr(user, 'role', False) - next_url = kwargs['next_url'] + role = getattr(user, "role", False) + next_url = kwargs["next_url"] if role: - if role == 'mentor': + if role == "mentor": account = get_object_or_404(Mentor, user=user) return self.update_account(request, account, next_url) account = get_object_or_404(Guardian, user=user) @@ -90,26 +80,23 @@ def post(self, request, *args, **kwargs): def update_account(self, request, account, next_url): if isinstance(account, Mentor): form = MentorForm(request.POST, instance=account) - role = 'mentor' + role = "mentor" else: form = GuardianForm(request.POST, instance=account) - role = 'guardian' + role = "guardian" if form.is_valid(): form.save() - messages.success(request, 'Profile information saved.') + messages.success(request, "Profile information saved.") if next_url: - if 'enroll' in request.GET: + if "enroll" in request.GET: next_url = f"{next_url}?enroll=True" else: - next_url = 'account_home' if isinstance(account, Mentor) else 'welcome' + next_url = "account_home" if isinstance(account, Mentor) else "welcome" return redirect(next_url) - return render(request, self.template_name, { - 'form': form, - 'role': role, - 'account': account, - 'next_url': next_url - }) + return render( + request, self.template_name, {"form": form, "role": role, "account": account, "next_url": next_url} + ) def add_student(self, request, account, next_url): form = StudentForm(request.POST) @@ -117,28 +104,26 @@ def add_student(self, request, account, next_url): new_student = form.save(commit=False) new_student.guardian = account new_student.save() - messages.success(request, 'Student Registered.') + messages.success(request, "Student Registered.") if next_url: - if 'enroll' in request.GET: + if "enroll" in request.GET: next_url = f"{next_url}?enroll=True&student={new_student.id}" else: - next_url = 'welcome' + next_url = "welcome" return redirect(next_url) - return render(request, self.template_name, { - 'form': form, - 'role': 'guardian', - 'account': account, - 'next_url': next_url, - 'add_student': True - }) + return render( + request, + self.template_name, + {"form": form, "role": "guardian", "account": account, "next_url": next_url, "add_student": True}, + ) def create_new_user(self, request, user, next_url): - if request.POST.get('role') == 'mentor': - role = 'mentor' + if request.POST.get("role") == "mentor": + role = "mentor" account, created = Mentor.objects.get_or_create(user=user) else: - role = 'guardian' + role = "guardian" account, created = Guardian.objects.get_or_create(user=user) account.user.first_name = user.first_name @@ -148,47 +133,38 @@ def create_new_user(self, request, user, next_url): user.role = role user.save() - merge_global_data = { - 'user': user.username, - 'first_name': user.first_name, - 'last_name': user.last_name - } + merge_global_data = {"user": user.username, "first_name": user.first_name, "last_name": user.last_name} next_url = f"?next={next_url}" if next_url else None - if role == 'mentor': + if role == "mentor": # check for next upcoming meeting - next_meeting = Meeting.objects.filter( - is_active=True, - is_public=True - ).order_by('start_date').first() + next_meeting = Meeting.objects.filter(is_active=True, is_public=True).order_by("start_date").first() if next_meeting: - merge_global_data['next_intro_meeting_url'] = f"{settings.SITE_URL}{next_meeting.get_absolute_url()}" - merge_global_data['next_intro_meeting_calendar_url'] = ( - f"{settings.SITE_URL}{next_meeting.get_calendar_url()}" - ) + merge_global_data["next_intro_meeting_url"] = f"{settings.SITE_URL}{next_meeting.get_absolute_url()}" + merge_global_data[ + "next_intro_meeting_calendar_url" + ] = f"{settings.SITE_URL}{next_meeting.get_calendar_url()}" if not next_url: - next_url = reverse('account_home') + next_url = reverse("account_home") else: # check for next upcoming class - next_class = Session.objects.filter( - is_active=True - ).order_by('start_date').first() + next_class = Session.objects.filter(is_active=True).order_by("start_date").first() if next_class: - merge_global_data['next_class_url'] = f"{settings.SITE_URL}{next_class.get_absolute_url()}" - merge_global_data['next_class_calendar_url'] = f"{settings.SITE_URL}{next_class.get_calendar_url()}" + merge_global_data["next_class_url"] = f"{settings.SITE_URL}{next_class.get_absolute_url()}" + merge_global_data["next_class_calendar_url"] = f"{settings.SITE_URL}{next_class.get_calendar_url()}" if not next_url: - next_url = reverse('welcome') + next_url = reverse("welcome") email( - subject='Welcome!', + subject="Welcome!", template_name=f"welcome-{role}", merge_global_data=merge_global_data, recipients=[user.email], - preheader='Your adventure awaits!', + preheader="Your adventure awaits!", ) return redirect(next_url) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..171bd3f5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,32 @@ +[tool.isort] +line_length = 120 +multi_line_output = 3 +include_trailing_comma = true +#default_section = "THIRDPARTY" +#known_first_party = [] +#known_third_party = [] +skip=['.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.tox', '.venv', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv', 'migrations'] + +[tool.black] +line-length = 120 +#target-version = ['py38'] +#include = '\.pyi?$' +exclude = ''' + +( + /( + \.eggs # exclude a few common directories in the + | \.git # root of the project + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + | migrations + )/ + +) +''' diff --git a/tasks.py b/tasks.py index 0483b9ab..f1c3d394 100644 --- a/tasks.py +++ b/tasks.py @@ -1,5 +1,3 @@ -import os - import environ from invoke import task @@ -13,34 +11,39 @@ def release(ctx): load_fixtures(ctx) -@task(help={'port': 'Port to use when serving traffic. Defaults to $PORT.'}) -def start(ctx, port=env.int('PORT', default=8000)): - ctx.run(f'gunicorn coderdojochi.wsgi -w 2 -b 0.0.0.0:{port} --reload --log-file -') +@task(help={"port": "Port to use when serving traffic. Defaults to $PORT."}) +def start(ctx, port=env.int("PORT", default=8000)): + ctx.run(f"gunicorn coderdojochi.wsgi -w 2 -b 0.0.0.0:{port} --reload --log-file -") @task def migrate(ctx): - ctx.run('python3 manage.py migrate') + ctx.run("python3 manage.py migrate") @task def load_fixtures(ctx): - if env.bool('ENABLE_DEV_FIXTURES', default=False): - ctx.run('python3 manage.py loaddata fixtures/*.json') + if env.bool("ENABLE_DEV_FIXTURES", default=False): + ctx.run("python3 manage.py loaddata fixtures/*.json") @task def collect_static(ctx): - if not env.bool('DEBUG', default=False): - ctx.run('python3 manage.py collectstatic --no-input') + if not env.bool("DEBUG", default=False): + ctx.run("python3 manage.py collectstatic --no-input") -@task(help={'app': 'Specific app to run tests on. Defaults to all apps.'}) -def test(ctx, app=''): - ctx.run(f'python3 manage.py test {app}') +@task(help={"app": "Specific app to run tests on. Defaults to all apps."}) +def test(ctx, app=""): + ctx.run(f"python3 manage.py test {app}") format(ctx) @task def format(ctx): - ctx.run('autopep8 -iaarj4 --exclude="**/migrations/*" --max-line-length="120" .') + # ctx.run('autopep8 -iaarj4 --exclude="**/migrations/*" --max-line-length="120" .') + ctx.run("black .") + ctx.run("autoflake --in-place --recursive --exclude '*/migrations/*' --remove-all-unused-imports .") + ctx.run("isort -m 3 --trailing-comma .") + ctx.run("black .") + ctx.run("isort -m 3 --trailing-comma .") diff --git a/weallcode/__init__.py b/weallcode/__init__.py index 812c5aa7..6ea20755 100644 --- a/weallcode/__init__.py +++ b/weallcode/__init__.py @@ -1 +1 @@ -default_app_config = 'weallcode.apps.WeAllCodeConfig' +default_app_config = "weallcode.apps.WeAllCodeConfig" diff --git a/weallcode/admin.py b/weallcode/admin.py index b8042bae..587ad4f1 100644 --- a/weallcode/admin.py +++ b/weallcode/admin.py @@ -8,44 +8,46 @@ class StaffMemberAdmin(admin.ModelAdmin): def member_image(self, obj): return format_html(f'') + member_image.allow_tags = True list_display = [ - 'member_image', - 'name', - 'role', - 'is_active', + "member_image", + "name", + "role", + "is_active", ] list_filter = [ - 'is_active', - 'role', + "is_active", + "role", ] readonly_fields = [ - 'member_image', - 'created_at', - 'updated_at', + "member_image", + "created_at", + "updated_at", ] - ordering = [ - 'name' - ] + ordering = ["name"] fieldsets = ( - (None, { - 'fields': ( - 'name', - 'role', - 'description', - 'linkedin', - 'image', - 'member_image', - 'is_active', - 'created_at', - 'updated_at', - ), - }), + ( + None, + { + "fields": ( + "name", + "role", + "description", + "linkedin", + "image", + "member_image", + "is_active", + "created_at", + "updated_at", + ), + }, + ), ) @@ -53,88 +55,91 @@ def member_image(self, obj): class BoardMemberAdmin(admin.ModelAdmin): def member_image(self, obj): return format_html(f'') + member_image.allow_tags = True list_display = [ - 'member_image', - 'name', - 'role', - 'is_active', + "member_image", + "name", + "role", + "is_active", ] list_filter = [ - 'is_active', - 'role', + "is_active", + "role", ] readonly_fields = [ - 'member_image', - 'created_at', - 'updated_at', + "member_image", + "created_at", + "updated_at", ] - ordering = [ - 'name' - ] + ordering = ["name"] fieldsets = ( - (None, { - 'fields': ( - 'name', - 'role', - 'description', - 'linkedin', - 'image', - 'member_image', - 'is_active', - 'created_at', - 'updated_at', - ), - }), + ( + None, + { + "fields": ( + "name", + "role", + "description", + "linkedin", + "image", + "member_image", + "is_active", + "created_at", + "updated_at", + ), + }, + ), ) @admin.register(AssociateBoardMember) class AssociateBoardMemberAdmin(admin.ModelAdmin): - def member_image(self, obj): return format_html(f'') + member_image.allow_tags = True list_display = [ - 'member_image', - 'name', - 'role', - 'is_active', + "member_image", + "name", + "role", + "is_active", ] list_filter = [ - 'is_active', - 'role', + "is_active", + "role", ] readonly_fields = [ - 'member_image', - 'created_at', - 'updated_at', + "member_image", + "created_at", + "updated_at", ] - ordering = [ - 'name' - ] + ordering = ["name"] fieldsets = ( - (None, { - 'fields': ( - 'name', - 'role', - 'description', - 'linkedin', - 'image', - 'member_image', - 'is_active', - 'created_at', - 'updated_at', - ), - }), + ( + None, + { + "fields": ( + "name", + "role", + "description", + "linkedin", + "image", + "member_image", + "is_active", + "created_at", + "updated_at", + ), + }, + ), ) diff --git a/weallcode/apps.py b/weallcode/apps.py index 77a38dca..0c3b8aa7 100644 --- a/weallcode/apps.py +++ b/weallcode/apps.py @@ -1,7 +1,6 @@ from django.apps import AppConfig -from django.utils.translation import gettext_lazy as _ class WeAllCodeConfig(AppConfig): - name = 'weallcode' - verbose_name = _("We All Code") + name = "weallcode" + verbose_name = "We All Code" diff --git a/weallcode/forms.py b/weallcode/forms.py index 85beb851..dbf01722 100644 --- a/weallcode/forms.py +++ b/weallcode/forms.py @@ -9,76 +9,55 @@ class ContactForm(forms.Form): widths = ( - ('name', 'small-6'), - ('email', 'small-6'), - ('interest', 'small-6'), - ('phone', 'small-6'), - ('message', 'small-12'), - ('captcha', ''), + ("name", "small-6"), + ("email", "small-6"), + ("interest", "small-6"), + ("phone", "small-6"), + ("message", "small-12"), + ("captcha", ""), ) - captcha = ReCaptchaField( - widget=ReCaptchaV3, - ) + captcha = ReCaptchaField(widget=ReCaptchaV3,) - name = forms.CharField( - max_length=100, - label='Full Name', - ) + name = forms.CharField(max_length=100, label="Full Name",) email = forms.EmailField( max_length=200, - label='Email Address', - widget=forms.TextInput( - attrs={ - 'type': 'email', - 'placeholder': 'email@example.com', - }, - ), + label="Email Address", + widget=forms.TextInput(attrs={"type": "email", "placeholder": "email@example.com",},), ) interest = forms.ChoiceField( choices=[ - ('volunteer', 'Volunteer'), - ('donate', 'Donate'), - ('sponsor', 'Sponsor'), - ('collaborate', 'Collaborate'), - ('other', 'Other'), + ("volunteer", "Volunteer"), + ("donate", "Donate"), + ("sponsor", "Sponsor"), + ("collaborate", "Collaborate"), + ("other", "Other"), ], - label='Topic of Interest', + label="Topic of Interest", ) phone = forms.CharField( max_length=20, - label='Phone Number', + label="Phone Number", widget=forms.TextInput( - attrs={ - 'type': 'tel', - 'placeholder': '+1 555 555-5555', - 'minlength': '10', - 'maxlength': '20', - }, + attrs={"type": "tel", "placeholder": "+1 555 555-5555", "minlength": "10", "maxlength": "20",}, ), ) message = forms.CharField( - label='Message', - widget=forms.Textarea( - attrs={ - 'placeholder': 'Enter your message', - 'minlength': 25, - 'maxlength': 500, - }, - ), + label="Message", + widget=forms.Textarea(attrs={"placeholder": "Enter your message", "minlength": 25, "maxlength": 500,},), ) def as_grid(self): - return ''.join([self.field_html(f[0], f[1]) for f in self.widths]) + return "".join([self.field_html(f[0], f[1]) for f in self.widths]) def field_html(self, field_name, field_classes): field = self[field_name] - if field_classes == '': + if field_classes == "": return f"{field}{field.errors}" return f"
{field.label_tag()}{field}{field.errors}
" @@ -90,9 +69,6 @@ def send_email(self): subject=f"{data['name']} | We All Code Contact Form", recipients=[settings.CONTACT_EMAIL], reply_to=[f"{data['name']}<{data['email']}>"], - template_name='contact-email', - merge_global_data={ - 'interest': data['interest'], - 'message': data['message'], - }, + template_name="contact-email", + merge_global_data={"interest": data["interest"], "message": data["message"],}, ) diff --git a/weallcode/models.py b/weallcode/models.py index 742af134..64ba59cf 100644 --- a/weallcode/models.py +++ b/weallcode/models.py @@ -1,68 +1,41 @@ -from docutils.nodes import description - from django.db import models -from django.utils.translation import gettext_lazy as _ -CHAIR = 'Chair' -VICE_CHAIR = 'Vice Chair' -SECRETARY = 'Secretary' -TREASURER = 'Treasurer' -DIRECTOR = 'Director' -PRESIDENT = 'President' +CHAIR = "Chair" +VICE_CHAIR = "Vice Chair" +SECRETARY = "Secretary" +TREASURER = "Treasurer" +DIRECTOR = "Director" +PRESIDENT = "President" ROLE_CHOICES = [ - (CHAIR, 'Chair'), - (VICE_CHAIR, 'Vice Chair'), - (SECRETARY, 'Secretary'), - (TREASURER, 'Treasurer'), - (DIRECTOR, 'Director'), - (PRESIDENT, 'President'), + (CHAIR, "Chair"), + (VICE_CHAIR, "Vice Chair"), + (SECRETARY, "Secretary"), + (TREASURER, "Treasurer"), + (DIRECTOR, "Director"), + (PRESIDENT, "President"), ] class CommonInfo(models.Model): - name = models.CharField( - max_length=255, - ) - - role = models.CharField( - choices=ROLE_CHOICES, - max_length=255, - default=DIRECTOR, - ) - - image = models.ImageField( - upload_to='staff/', - blank=True, - null=True, - ) - - description = models.CharField( - max_length=255, - blank=True, - null=True, - ) - - linkedin = models.URLField( - max_length=200, - blank=True, - null=True, - ) + name = models.CharField(max_length=255,) + + role = models.CharField(choices=ROLE_CHOICES, max_length=255, default=DIRECTOR,) + + image = models.ImageField(upload_to="staff/", blank=True, null=True,) + + description = models.CharField(max_length=255, blank=True, null=True,) + + linkedin = models.URLField(max_length=200, blank=True, null=True,) # Active - is_active = models.BooleanField( - default=True, - ) + is_active = models.BooleanField(default=True,) # Auto create/update - created_at = models.DateTimeField( - auto_now_add=True, - ) + created_at = models.DateTimeField(auto_now_add=True,) - updated_at = models.DateTimeField( - auto_now=True, - ) + updated_at = models.DateTimeField(auto_now=True,) class Meta: abstract = True @@ -70,19 +43,9 @@ class Meta: class StaffMember(CommonInfo): - role = models.CharField( - max_length=255, - ) + role = models.CharField(max_length=255,) - image = models.ImageField( - upload_to='staff/', - blank=True, - null=True, - ) - - class Meta: - verbose_name = _("Staff Member") - verbose_name_plural = _("Staff Members") + image = models.ImageField(upload_to="staff/", blank=True, null=True,) def __str__(self): return self.name @@ -90,15 +53,7 @@ def __str__(self): class BoardMember(CommonInfo): - image = models.ImageField( - upload_to='board/', - blank=True, - null=True, - ) - - class Meta: - verbose_name = _("Board Member") - verbose_name_plural = _("Board Members") + image = models.ImageField(upload_to="board/", blank=True, null=True,) def __str__(self): return self.name @@ -106,15 +61,7 @@ def __str__(self): class AssociateBoardMember(CommonInfo): - image = models.ImageField( - upload_to='associate-board/', - blank=True, - null=True, - ) - - class Meta: - verbose_name = _("Associate Board Member") - verbose_name_plural = _("Associate Board Members") + image = models.ImageField(upload_to="associate-board/", blank=True, null=True,) def __str__(self): return self.name diff --git a/weallcode/templatetags/active.py b/weallcode/templatetags/active.py index 7e03ba32..004ee9cd 100644 --- a/weallcode/templatetags/active.py +++ b/weallcode/templatetags/active.py @@ -7,10 +7,10 @@ @register.simple_tag(takes_context=True) -def active(context, pattern_or_urlname, css_class='active'): +def active(context, pattern_or_urlname, css_class="active"): try: - pattern = '^' + reverse(pattern_or_urlname) + pattern = "^" + reverse(pattern_or_urlname) except NoReverseMatch: pattern = pattern_or_urlname - return css_class if re.search(pattern, context['request'].path) else '' + return css_class if re.search(pattern, context["request"].path) else "" diff --git a/weallcode/tests.py b/weallcode/tests.py index 7ce503c2..a39b155a 100644 --- a/weallcode/tests.py +++ b/weallcode/tests.py @@ -1,3 +1 @@ -from django.test import TestCase - # Create your tests here. diff --git a/weallcode/urls.py b/weallcode/urls.py index c19040c2..0f4cba3a 100644 --- a/weallcode/urls.py +++ b/weallcode/urls.py @@ -17,31 +17,38 @@ ) sitemaps = { - 'static': StaticSitemapView, + "static": StaticSitemapView, } urlpatterns = [ - path('', HomeView.as_view(), name='weallcode-home'), - path('our-story/', OurStoryView.as_view(), name='weallcode-our-story'), - path('programs/', include([ - path('', ProgramsView.as_view(), name='weallcode-programs'), - path('summer-camps/', ProgramsSummerCampsView.as_view(), name='weallcode-programs-summer-camps'), - ])), - path('team/', TeamView.as_view(), name='weallcode-team'), - path('join-us/', include([ - path('', JoinUsView.as_view(), name='weallcode-join-us'), - path('associate-board/', AssociateBoardView.as_view(), name='weallcode-associate-board'), - ])), - path('privacy/', PrivacyView.as_view(), name='weallcode-privacy'), - path('credits/', CreditsView.as_view(), name='weallcode-credits'), - - path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'), - + path("", HomeView.as_view(), name="weallcode-home"), + path("our-story/", OurStoryView.as_view(), name="weallcode-our-story"), + path( + "programs/", + include( + [ + path("", ProgramsView.as_view(), name="weallcode-programs"), + path("summer-camps/", ProgramsSummerCampsView.as_view(), name="weallcode-programs-summer-camps"), + ] + ), + ), + path("team/", TeamView.as_view(), name="weallcode-team"), + path( + "join-us/", + include( + [ + path("", JoinUsView.as_view(), name="weallcode-join-us"), + path("associate-board/", AssociateBoardView.as_view(), name="weallcode-associate-board"), + ] + ), + ), + path("privacy/", PrivacyView.as_view(), name="weallcode-privacy"), + path("credits/", CreditsView.as_view(), name="weallcode-credits"), + path("sitemap.xml", sitemap, {"sitemaps": sitemaps}, name="django.contrib.sitemaps.views.sitemap"), # Redirect /summer-camps/ to weallcode-programs-summer-camps - path('summer-camps/', RedirectView.as_view(pattern_name='weallcode-programs-summer-camps')), - + path("summer-camps/", RedirectView.as_view(pattern_name="weallcode-programs-summer-camps")), # Redirect /get-involved/ to weallcode-join-us - path('get-involved/', RedirectView.as_view(pattern_name='weallcode-join-us')), + path("get-involved/", RedirectView.as_view(pattern_name="weallcode-join-us")), ] -handler404 = 'weallcode.views.page_not_found_view' +handler404 = "weallcode.views.page_not_found_view" diff --git a/weallcode/views.py b/weallcode/views.py index b316658d..4dc8d80b 100644 --- a/weallcode/views.py +++ b/weallcode/views.py @@ -6,7 +6,6 @@ from django.contrib import messages, sitemaps from django.contrib.auth import get_user_model from django.db.models import Count -from django.http import HttpResponseNotFound from django.urls import reverse, reverse_lazy from django.utils import timezone from django.views.generic import FormView, TemplateView @@ -23,13 +22,12 @@ def page_not_found_view(*args, **kwargs): - print('page_not_found_view') + print("page_not_found_view") capture_message("Page not found!", level="error") - return render(request, "404.html", { - 'sentry_event_id': last_event_id(), - 'SENTRY_DSN': settings.SENTRY_DSN, - }, status=404) + return render( + request, "404.html", {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, status=404 + ) class DefaultMetaTags(MetadataMixin): @@ -46,38 +44,32 @@ class DefaultMetaTags(MetadataMixin): class HomeView(DefaultMetaTags, TemplateView): template_name = "weallcode/home.html" - url = reverse_lazy('weallcode-home') + url = reverse_lazy("weallcode-home") def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - sessions = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now() - ).order_by('start_date') + sessions = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") - if ( - not self.request.user.is_authenticated or - not self.request.user.role == 'mentor' - ): + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": sessions = sessions.filter(is_public=True) if len(sessions) > 0: - context['next_session'] = sessions[0] + context["next_session"] = sessions[0] return context class OurStoryView(DefaultMetaTags, TemplateView): template_name = "weallcode/our_story.html" - url = reverse_lazy('weallcode-our-story') + url = reverse_lazy("weallcode-our-story") title = f"Our Story | {settings.SITE_NAME}" class ProgramsView(DefaultMetaTags, TemplateView): template_name = "weallcode/programs.html" - url = reverse_lazy('weallcode-programs') + url = reverse_lazy("weallcode-programs") title = f"Programs | {settings.SITE_NAME}" @@ -86,40 +78,30 @@ def get_context_data(self, **kwargs): # WEEKEND CLASSES weekend_classes = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now(), - course__course_type=Course.WEEKEND, - ).order_by('start_date') - - if ( - not self.request.user.is_authenticated or - not self.request.user.role == 'mentor' - ): + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.WEEKEND, + ).order_by("start_date") + + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": weekend_classes = weekend_classes.filter(is_public=True) - context['weekend_classes'] = weekend_classes + context["weekend_classes"] = weekend_classes # SUMMER CAMP CLASSES summer_camp_classes = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now(), - course__course_type=Course.CAMP, - ).order_by('start_date') - - if ( - not self.request.user.is_authenticated or - not self.request.user.role == 'mentor' - ): + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, + ).order_by("start_date") + + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": summer_camp_classes = summer_camp_classes.filter(is_public=True) - context['summer_camp_classes'] = summer_camp_classes + context["summer_camp_classes"] = summer_camp_classes return context class ProgramsSummerCampsView(DefaultMetaTags, TemplateView): template_name = "weallcode/programs-summer-camps.html" - url = reverse_lazy('weallcode-programs-summer-camps') + url = reverse_lazy("weallcode-programs-summer-camps") title = f"Summer Camps | {settings.SITE_NAME}" @@ -128,41 +110,32 @@ def get_context_data(self, **kwargs): # SUMMER CAMP CLASSES summer_camp_classes = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now(), - course__course_type=Course.CAMP, - ).order_by('start_date') - - if ( - not self.request.user.is_authenticated or - not self.request.user.role == 'mentor' - ): + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, + ).order_by("start_date") + + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": summer_camp_classes = summer_camp_classes.filter(is_public=True) - context['summer_camp_classes'] = summer_camp_classes + context["summer_camp_classes"] = summer_camp_classes return context class TeamView(DefaultMetaTags, TemplateView): template_name = "weallcode/team.html" - url = reverse_lazy('weallcode-team') + url = reverse_lazy("weallcode-team") title = f"Team | {settings.SITE_NAME}" # Staff def get_staff(self, context): - context['staff'] = StaffMember.objects.filter( - is_active=True, - ) + context["staff"] = StaffMember.objects.filter(is_active=True,) return context # Board Members def get_board(self, context): - board = BoardMember.objects.filter( - is_active=True, - ) + board = BoardMember.objects.filter(is_active=True,) # NOTE: We're splitting and reordering them manually since the website requires a specific order. roles = defaultdict(list) @@ -171,21 +144,15 @@ def get_board(self, context): roles[person.role].append(person) # The correct order - context['board'] = list(chain( - roles['Chair'], - roles['Vice Chair'], - roles['Treasurer'], - roles['Secretary'], - roles['Director'], - )) + context["board"] = list( + chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) + ) return context # Associate Board Members def get_associate_board(self, context): - associate_board = AssociateBoardMember.objects.filter( - is_active=True, - ).order_by('name') + associate_board = AssociateBoardMember.objects.filter(is_active=True,).order_by("name") # NOTE: We're splitting and reordering them manually since the website requires a specific order. roles = defaultdict(list) @@ -194,29 +161,23 @@ def get_associate_board(self, context): roles[person.role].append(person) # The correct order - context['associate_board'] = list(chain( - roles['Chair'], - roles['Vice Chair'], - roles['Treasurer'], - roles['Secretary'], - roles['Director'], - )) + context["associate_board"] = list( + chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) + ) return context # Instructors def get_instructors(self, context, volunteers): - context['instructors'] = volunteers.filter( - user__groups__name__in=['Instructor'], - ).order_by('user__first_name') + context["instructors"] = volunteers.filter(user__groups__name__in=["Instructor"],).order_by("user__first_name") return context # Volunteers def get_volunteers(self, context, volunteers): - all_volunteers = volunteers.annotate( - session_count=Count('mentororder') - ).order_by('-user__role', '-session_count') + all_volunteers = volunteers.annotate(session_count=Count("mentororder")).order_by( + "-user__role", "-session_count" + ) mentors = [] volunteers = [] @@ -227,21 +188,18 @@ def get_volunteers(self, context, volunteers): else: volunteers += [volunteer] - context['top_mentors'] = mentors + context["top_mentors"] = mentors # context['other_mentors'] = mentors[8:] - context['top_volunteers'] = volunteers[0:16] - context['other_volunteers'] = volunteers[16:] + context["top_volunteers"] = volunteers[0:16] + context["other_volunteers"] = volunteers[16:] return context def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - volunteers = Mentor.objects.select_related('user').filter( - is_active=True, - is_public=True, - background_check=True, - avatar_approved=True, + volunteers = Mentor.objects.select_related("user").filter( + is_active=True, is_public=True, background_check=True, avatar_approved=True, ) context = self.get_staff(context) @@ -256,8 +214,8 @@ def get_context_data(self, **kwargs): class JoinUsView(DefaultMetaTags, FormView): template_name = "weallcode/join_us.html" form_class = ContactForm - url = reverse_lazy('weallcode-join-us') - success_url = reverse_lazy('weallcode-join-us') + url = reverse_lazy("weallcode-join-us") + success_url = reverse_lazy("weallcode-join-us") title = f"Join Us | {settings.SITE_NAME}" @@ -265,49 +223,46 @@ def form_valid(self, form): # This method is called when valid form data has been POSTed. # It should return an HttpResponse. form.send_email() - messages.success( - self.request, - "Thank you for contacting us! We will respond as soon as possible." - ) + messages.success(self.request, "Thank you for contacting us! We will respond as soon as possible.") return super().form_valid(form) class AssociateBoardView(DefaultMetaTags, TemplateView): template_name = "weallcode/associate_board.html" - url = reverse_lazy('weallcode-associate-board') + url = reverse_lazy("weallcode-associate-board") title = f"Join our Associate Board | {settings.SITE_NAME}" class PrivacyView(DefaultMetaTags, TemplateView): template_name = "weallcode/privacy.html" - url = reverse_lazy('weallcode-privacy') + url = reverse_lazy("weallcode-privacy") title = f"Privacy & Terms | {settings.SITE_NAME}" class CreditsView(DefaultMetaTags, TemplateView): template_name = "weallcode/credits.html" - url = reverse_lazy('weallcode-credits') + url = reverse_lazy("weallcode-credits") title = f"Credits & Attributions | {settings.SITE_NAME}" class StaticSitemapView(sitemaps.Sitemap): priority = 0.5 - changefreq = 'daily' + changefreq = "daily" def items(self): return [ - 'weallcode-home', - 'weallcode-our-story', - 'weallcode-programs', - 'weallcode-programs-summer-camps', - 'weallcode-team', - 'weallcode-join-us', - 'weallcode-privacy', - 'weallcode-credits', + "weallcode-home", + "weallcode-our-story", + "weallcode-programs", + "weallcode-programs-summer-camps", + "weallcode-team", + "weallcode-join-us", + "weallcode-privacy", + "weallcode-credits", ] def location(self, item): From 66ea3ef1430c17593f8eb4d7d2f5402025f50b13 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Mon, 24 Aug 2020 08:57:09 -0500 Subject: [PATCH 02/57] Upgrade python 3.8.3 to 3.8.5 --- Dockerfile | 2 +- runtime.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 727dc03e..5f263c9f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8.3 +FROM python:3.8.5 # Force stdin, stdout and stderr to be totally unbuffered. ENV PYTHONUNBUFFERED 1 diff --git a/runtime.txt b/runtime.txt index 385705b5..43b47fb4 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -python-3.8.3 +python-3.8.5 From fab107db6cdc0dbf05740b8f3c1b3341a5272750 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Mon, 24 Aug 2020 09:13:48 -0500 Subject: [PATCH 03/57] Update packages --- Pipfile.lock | 99 ++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 0b3732b5..3e0eb973 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -18,11 +18,11 @@ "default": { "arrow": { "hashes": [ - "sha256:271b8e05174d48e50324ed0dc5d74796c839c7e579a4f21cf1a7394665f9e94f", - "sha256:edc31dc051db12c95da9bac0271cd1027b8e36912daf6d4580af53b23e62721a" + "sha256:92aac856ea5175c804f7ccb96aca4d714d936f1c867ba59d747a8096ec30e90a", + "sha256:98184d8dd3e5d30b96c2df4596526f7de679ccb467f358b82b0f686436f3a6b8" ], "index": "pypi", - "version": "==0.15.8" + "version": "==0.16.0" }, "asgiref": { "hashes": [ @@ -34,18 +34,18 @@ }, "boto3": { "hashes": [ - "sha256:a88d486dcbc80c3e180d811707b366f7b178cf293e0746ae3c00c24a07deac92", - "sha256:c9cab4e0ce77a8c54724eadff047adc976e541f912ca15e35bb475f42b344e0c" + "sha256:0d9cbeb5c8ca67650cc963c77e2e3b3ab5dffeeee16e03d61d740755f8fc7c44", + "sha256:df73edf3bd6f191870212e04ae9a8bc6245fd6749f464e9fb950392a8d15bd8c" ], "index": "pypi", - "version": "==1.14.40" + "version": "==1.14.47" }, "botocore": { "hashes": [ - "sha256:c3fad73aefabc89c918687a0f207772925530017ce1ca58e2a47b459a1060cb0", - "sha256:feb71e0d2e73fed3c35f1ebfc6b9ff6e499dc9c66db60b63c787fffea3b360a5" + "sha256:42b320b449df22cdb1232913e4a066919d127feb8e58ad98898831e6255ccfe0", + "sha256:eca25f01c503c2b86b394497f875a0eb0d3fe367dbc032f3a02851ba7e827109" ], - "version": "==1.17.40" + "version": "==1.17.47" }, "certifi": { "hashes": [ @@ -209,11 +209,11 @@ }, "django-nose": { "hashes": [ - "sha256:58934a06a6932696e54c9e8af3fab49bf67ca9e9c840ad668cb7f51219808a07", - "sha256:f515d903cfaeda52c7a9198e0c8ed51563e82802b62a4e5fbd056fd830095318" + "sha256:304adc447ee35b889b733d7106004f98aa401d8387ddcada5d4f2239d86790a9", + "sha256:a4885cd002d65fd2de96e2bb2563ef477c3fbe207009360c015fca5c3b5561b7" ], "index": "pypi", - "version": "==1.4.6" + "version": "==1.4.7" }, "django-recaptcha": { "hashes": [ @@ -255,19 +255,18 @@ }, "factory-boy": { "hashes": [ - "sha256:728df59b372c9588b83153facf26d3d28947fc750e8e3c95cefa9bed0e6394ee", - "sha256:faf48d608a1735f0d0a3c9cbf536d64f9132b547dae7ba452c4d99a79e84a370" + "sha256:2ce2f665045d9f15145a6310565fcb8255d52fc6fd867f3b783b3ac3de6cf10e" ], "index": "pypi", - "version": "==2.12.0" + "version": "==3.0.1" }, "faker": { "hashes": [ - "sha256:1290f589648bc470b8d98fff1fdff773fe3f46b4ca2cac73ac74668b12cf008e", - "sha256:c006b3664c270a2cfd4785c5e41ff263d48101c4e920b5961cf9c237131d8418" + "sha256:bc4b8c908dfcd84e4fe5d9fa2e52fbe17546515fb8f126909b98c47badf05658", + "sha256:ff188c416864e3f7d8becd8f9ee683a4b4101a2a2d2bcdcb3e84bb1bdd06eaae" ], - "markers": "python_version >= '3.4'", - "version": "==4.1.1" + "markers": "python_version >= '3.5'", + "version": "==4.1.2" }, "gunicorn": { "hashes": [ @@ -363,39 +362,41 @@ }, "openpyxl": { "hashes": [ - "sha256:6e62f058d19b09b95d20ebfbfb04857ad08d0833190516c1660675f699c6186f", - "sha256:d88dd1480668019684c66cfff3e52a5de4ed41e9df5dd52e008cbf27af0dbf87" + "sha256:18e11f9a650128a12580a58e3daba14e00a11d9e907c554a17ea016bf1a2c71b", + "sha256:f7d666b569f729257082cf7ddc56262431878f602dcc2bc3980775c59439cdab" ], - "version": "==3.0.4" + "version": "==3.0.5" }, "pillow": { "hashes": [ - "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", - "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", - "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", - "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", - "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae", - "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", - "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", - "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", - "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", - "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", - "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", + "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", "sha256:6edb5446f44d901e8683ffb25ebdfc26988ee813da3bf91e12252b57ac163727", - "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", + "sha256:6d7741e65835716ceea0fd13a7d0192961212fd59e741a46bbed7a473c634ed6", + "sha256:612cfda94e9c8346f239bf1a4b082fdd5c8143cf82d685ba2dba76e7adeeb233", "sha256:8dad18b69f710bf3a001d2bf3afab7c432785d94fcf819c16b5207b1cfd17d38", - "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", - "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", - "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", - "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", + "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", "sha256:c79f9c5fb846285f943aafeafda3358992d64f0ef58566e23484132ecd8d7d63", + "sha256:97f9e7953a77d5a70f49b9a48da7776dc51e9b738151b22dacf101641594a626", + "sha256:0a80dd307a5d8440b0a08bd7b81617e04d870e40a3e46a32d9c246e54705e86f", + "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", "sha256:c92302a33138409e8f1ad16731568c55c9053eee71bb05b6b744067e1b62380f", + "sha256:a060cf8aa332052df2158e5a119303965be92c3da6f2d93b6878f0ebca80b2f6", "sha256:d08b23fdb388c0715990cbc06866db554e1822c4bdcf6d4166cf30ac82df8c41", - "sha256:d350f0f2c2421e65fbc62690f26b59b0bcda1b614beb318c81e38647e0f673a1", - "sha256:ec29604081f10f16a7aea809ad42e27764188fc258b02259a03a8ff7ded3808d", - "sha256:edf31f1150778abd4322444c393ab9c7bd2af271dd4dafb4208fb613b1f3cdc9", + "sha256:25930fadde8019f374400f7986e8404c8b781ce519da27792cbe46eabec00c4d", "sha256:f7e30c27477dffc3e85c2463b3e649f751789e0f6c8456099eea7ddd53be4a8a", - "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce" + "sha256:52125833b070791fcb5710fabc640fc1df07d087fc0c0f02d3661f76c23c5b8b", + "sha256:09d7f9e64289cb40c2c8d7ad674b2ed6105f55dc3b09aa8e4918e20a0311e7ad", + "sha256:725aa6cfc66ce2857d585f06e9519a1cc0ef6d13f186ff3447ab6dff0a09bc7f", + "sha256:5e51ee2b8114def244384eda1c82b10e307ad9778dac5c83fb0943775a653cd8", + "sha256:06aba4169e78c439d528fdeb34762c3b61a70813527a2c57f0540541e9f433a8", + "sha256:e901964262a56d9ea3c2693df68bc9860b8bdda2b04768821e4c44ae797de117", + "sha256:94cf49723928eb6070a892cb39d6c156f7b5a2db4e8971cb958f7b6b104fb4c4", + "sha256:0295442429645fa16d05bd567ef5cff178482439c9aad0411d3f0ce9b88b3a6f", + "sha256:ffe538682dc19cc542ae7c3e504fdf54ca7f86fb8a135e59dd6bc8627eae6cce", + "sha256:431b15cffbf949e89df2f7b48528be18b78bfa5177cb3036284a5508159492b5", + "sha256:9c87ef410a58dd54b92424ffd7e28fd2ec65d2f7fc02b76f5e9b2067e355ebf6", + "sha256:9ad7f865eebde135d526bb3163d0b23ffff365cf87e767c649550964ad72785d", + "sha256:1ca594126d3c4def54babee699c055a913efb01e106c309fa6b04405d474d5ae" ], "index": "pypi", "version": "==7.2.0" @@ -624,11 +625,11 @@ }, "isort": { "hashes": [ - "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", - "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd" + "sha256:60a1b97e33f61243d12647aaaa3e6cc6778f5eb9f42997650f1cc975b6008750", + "sha256:d488ba1c5a2db721669cc180180d5acf84ebdc5af7827f7aaeaa75f73cf0e2b8" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==4.3.21" + "markers": "python_version >= '3.6' and python_version < '4.0'", + "version": "==5.4.2" }, "lazy-object-proxy": { "hashes": [ @@ -674,11 +675,11 @@ }, "pylint": { "hashes": [ - "sha256:7dd78437f2d8d019717dbf287772d0b2dbdfd13fc016aa7faa08d67bccc46adc", - "sha256:d0ece7d223fe422088b0e8f13fa0a1e8eb745ebffcb8ed53d3e95394b6101a1c" + "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210", + "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f" ], "index": "pypi", - "version": "==2.5.3" + "version": "==2.6.0" }, "six": { "hashes": [ From 8f573a0f9c05dd8bc673600e960e53498845183a Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Mon, 28 Dec 2020 21:22:14 -0600 Subject: [PATCH 04/57] Format --- .vscode/settings.json | 24 ++++- accounts/views.py | 52 ++++------- coderdojochi/admin.py | 113 +++++++++++++---------- coderdojochi/settings.py | 21 ++--- weallcode/templates/weallcode/_base.html | 1 + weallcode/views.py | 74 ++++----------- 6 files changed, 125 insertions(+), 160 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index bbc6235e..31a0b0a1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,12 +15,20 @@ "emmet.includeLanguages": { "django-html": "html" }, + "beautify.language": { + "js": { + "type": ["javascript", "json", "jsonc"], + "filename": [".jshintrc", ".jsbeautifyrc"] + }, + "css": ["css", "less", "scss"], + "html": ["htm", "html", "django-html"] + }, + "python.formatting.provider": "black", "python.linting.ignorePatterns": [ ".vscode/*.py", "**/site-packages/**/*.py", "**/migrations/**" ], - "python.sortImports.args": ["--settings-path=${workspaceFolder}/setup.cfg"], "python.linting.enabled": true, "python.linting.pycodestyleEnabled": false, "python.linting.pycodestylePath": "pycodestyle", @@ -36,12 +44,18 @@ "[python]": { "editor.rulers": [120], "editor.codeActionsOnSave": { - // it was `"source.organizeImports": true` in my first version of this post, - // see below comment for explanation. - "source.organizeImports.python": true + "source.organizeImports": true, + "source.fixAll": true + } + }, + "[django-html]": { + "editor.defaultFormatter": "HookyQR.beautify", + "editor.quickSuggestions": { + "other": true, + "comments": true, + "strings": true } }, - "[git-commit]": { "editor.rulers": [72] }, diff --git a/accounts/views.py b/accounts/views.py index ba82aff4..07520990 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -11,14 +11,7 @@ from meta.views import MetadataMixin from coderdojochi.forms import CDCModelForm, GuardianForm, MentorForm -from coderdojochi.models import ( - Guardian, - MeetingOrder, - Mentor, - MentorOrder, - Order, - Student, -) +from coderdojochi.models import Guardian, MeetingOrder, Mentor, MentorOrder, Order, Student class SignupView(MetadataMixin, AllAuthSignupView): @@ -57,8 +50,7 @@ def dispatch(self, *args, **kwargs): return redirect(f"{reverse('welcome')}?next={self.request.GET['next']}") else: messages.warning( - self.request, - "Tell us a little about yourself before going on account.", + self.request, "Tell us a little about yourself before going on account.", ) return redirect("welcome") @@ -67,9 +59,7 @@ def dispatch(self, *args, **kwargs): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["highlight"] = ( - self.request.GET["highlight"] if "highlight" in self.request.GET else False - ) + context["highlight"] = self.request.GET["highlight"] if "highlight" in self.request.GET else False context["user"] = self.request.user @@ -84,24 +74,20 @@ def get_context_data(self, **kwargs): def get_context_data_for_mentor(self): mentor = get_object_or_404(Mentor, user=self.request.user) - orders = MentorOrder.objects.select_related().filter( - is_active=True, mentor=mentor, - ) + orders = MentorOrder.objects.select_related().filter(is_active=True, mentor=mentor,) - upcoming_sessions = orders.filter( - is_active=True, session__start_date__gte=timezone.now() - ).order_by("session__start_date") + upcoming_sessions = orders.filter(is_active=True, session__start_date__gte=timezone.now()).order_by( + "session__start_date" + ) - past_sessions = orders.filter( - is_active=True, session__start_date__lte=timezone.now() - ).order_by("session__start_date") + past_sessions = orders.filter(is_active=True, session__start_date__lte=timezone.now()).order_by( + "session__start_date" + ) meeting_orders = MeetingOrder.objects.select_related().filter(mentor=mentor) upcoming_meetings = meeting_orders.filter( - is_active=True, - meeting__is_public=True, - meeting__end_date__gte=timezone.now(), + is_active=True, meeting__is_public=True, meeting__end_date__gte=timezone.now(), ).order_by("meeting__start_date") account_complete = False @@ -132,13 +118,13 @@ def get_context_data_for_guardian(self): student_orders = Order.objects.filter(student__in=students,) - upcoming_orders = student_orders.filter( - is_active=True, session__start_date__gte=timezone.now(), - ).order_by("session__start_date") + upcoming_orders = student_orders.filter(is_active=True, session__start_date__gte=timezone.now(),).order_by( + "session__start_date" + ) - past_orders = student_orders.filter( - is_active=True, session__start_date__lte=timezone.now(), - ).order_by("session__start_date") + past_orders = student_orders.filter(is_active=True, session__start_date__lte=timezone.now(),).order_by( + "session__start_date" + ) return { "guardian": guardian, @@ -185,9 +171,7 @@ def post_for_mentor(self, **kwargs): form = MentorForm(self.request.POST, self.request.FILES, instance=mentor) - user_form = CDCModelForm( - self.request.POST, self.request.FILES, instance=mentor.user - ) + user_form = CDCModelForm(self.request.POST, self.request.FILES, instance=mentor.user) if form.is_valid() and user_form.is_valid(): form.save() diff --git a/coderdojochi/admin.py b/coderdojochi/admin.py index 009b0aec..ff6a606d 100644 --- a/coderdojochi/admin.py +++ b/coderdojochi/admin.py @@ -513,57 +513,68 @@ class SessionAdmin(ImportExportMixin, ImportExportActionModelAdmin): date_hierarchy = "start_date" fieldsets = ( - (None, { - # 'classes': ('collapse',), - 'fields': ( - 'course', - 'location', - 'start_date', - # 'old_end_date', - # 'old_mentor_start_date', - # 'old_mentor_end_date', - 'capacity', - 'mentor_capacity', - 'instructor', - 'is_active', - 'is_public', - ), - }), - ('Zoom', { - 'fields': ( - 'online_video_link', - 'online_video_meeting_id', - 'online_video_meeting_password', - # 'online_video_description', - ), - }), - ('Overrides', { - # 'classes': ('collapse',), - 'fields': ( - 'override_minimum_age_limitation', - 'override_maximum_age_limitation', - 'gender_limitation', - 'cost', - 'minimum_cost', - 'maximum_cost', - - ) - }), - ('Advanced options', { - 'classes': ('collapse',), - 'fields': ( - 'password', - 'partner_message', - 'additional_info', - 'external_enrollment_url', - 'image_url', - 'bg_image', - 'announced_date_mentors', - 'announced_date_guardians', - 'mentors_week_reminder_sent', - 'mentors_day_reminder_sent', - ), - }), + ( + None, + { + # 'classes': ('collapse',), + "fields": ( + "course", + "location", + "start_date", + # 'old_end_date', + # 'old_mentor_start_date', + # 'old_mentor_end_date', + "capacity", + "mentor_capacity", + "instructor", + "is_active", + "is_public", + ), + }, + ), + ( + "Zoom", + { + "fields": ( + "online_video_link", + "online_video_meeting_id", + "online_video_meeting_password", + # 'online_video_description', + ), + }, + ), + ( + "Overrides", + { + # 'classes': ('collapse',), + "fields": ( + "override_minimum_age_limitation", + "override_maximum_age_limitation", + "gender_limitation", + "cost", + "minimum_cost", + "maximum_cost", + ) + }, + ), + ( + "Advanced options", + { + "classes": ("collapse",), + "fields": ( + "password", + "partner_message", + "additional_info", + "external_enrollment_url", + "image_url", + "bg_image", + "announced_date_mentors", + "announced_date_guardians", + "mentors_week_reminder_sent", + "mentors_day_reminder_sent", + ), + }, + ), ) def view_on_site(self, obj): diff --git a/coderdojochi/settings.py b/coderdojochi/settings.py index a1d3411a..93475cdc 100644 --- a/coderdojochi/settings.py +++ b/coderdojochi/settings.py @@ -193,8 +193,8 @@ USE_TZ = True SITE_ID = 1 -SITE_NAME = 'We All Code' -SITE_URL = env('SITE_URL', default=None) +SITE_NAME = "We All Code" +SITE_URL = env("SITE_URL", default=None) # Honor the 'X-Forwarded-Proto' header for request.is_secure() SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") @@ -310,17 +310,12 @@ def MediaRootS3BotoStorage(): META_USE_TWITTER_PROPERTIES = True META_USE_SCHEMAORG_PROPERTIES = True # META_USE_TITLE_TAG = True -META_TWITTER_SITE = env('META_TWITTER_SITE', default='@weallcode') -META_FB_APPID = env('META_SITE_DOMAIN', default='1454178301519376') -META_INCLUDE_KEYWORDS = env.list('META_INCLUDE_KEYWORDS', default=[ - 'stem', - 'code', - 'coding', - 'kids', - 'chicago', - 'chicago coding' -]) -DEFAULT_META_TITLE = env('DEFAULT_META_TITLE', default='') +META_TWITTER_SITE = env("META_TWITTER_SITE", default="@weallcode") +META_FB_APPID = env("META_SITE_DOMAIN", default="1454178301519376") +META_INCLUDE_KEYWORDS = env.list( + "META_INCLUDE_KEYWORDS", default=["stem", "code", "coding", "kids", "chicago", "chicago coding"] +) +DEFAULT_META_TITLE = env("DEFAULT_META_TITLE", default="") # django allauth diff --git a/weallcode/templates/weallcode/_base.html b/weallcode/templates/weallcode/_base.html index 51aeb4a0..dfb162a1 100644 --- a/weallcode/templates/weallcode/_base.html +++ b/weallcode/templates/weallcode/_base.html @@ -95,6 +95,7 @@ ga('create', 'UA-78618586-1', 'auto'); ga('set', 'transport', 'beacon'); ga('send', 'pageview'); + diff --git a/weallcode/views.py b/weallcode/views.py index 2ebd6eed..49cda5bf 100644 --- a/weallcode/views.py +++ b/weallcode/views.py @@ -26,10 +26,7 @@ def page_not_found_view(*args, **kwargs): capture_message("Page not found!", level="error") return render( - request, - "404.html", - {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, - status=404, + request, "404.html", {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, status=404, ) @@ -52,14 +49,9 @@ class HomeView(DefaultMetaTags, TemplateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - sessions = Session.objects.filter( - is_active=True, start_date__gte=timezone.now() - ).order_by("start_date") + sessions = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") - if ( - not self.request.user.is_authenticated - or not self.request.user.role == "mentor" - ): + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": sessions = sessions.filter(is_public=True) if len(sessions) > 0: @@ -86,30 +78,20 @@ def get_context_data(self, **kwargs): # WEEKEND CLASSES weekend_classes = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now(), - course__course_type=Course.WEEKEND, + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.WEEKEND, ).order_by("start_date") - if ( - not self.request.user.is_authenticated - or not self.request.user.role == "mentor" - ): + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": weekend_classes = weekend_classes.filter(is_public=True) context["weekend_classes"] = weekend_classes # SUMMER CAMP CLASSES summer_camp_classes = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now(), - course__course_type=Course.CAMP, + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, ).order_by("start_date") - if ( - not self.request.user.is_authenticated - or not self.request.user.role == "mentor" - ): + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": summer_camp_classes = summer_camp_classes.filter(is_public=True) context["summer_camp_classes"] = summer_camp_classes @@ -128,15 +110,10 @@ def get_context_data(self, **kwargs): # SUMMER CAMP CLASSES summer_camp_classes = Session.objects.filter( - is_active=True, - start_date__gte=timezone.now(), - course__course_type=Course.CAMP, + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, ).order_by("start_date") - if ( - not self.request.user.is_authenticated - or not self.request.user.role == "mentor" - ): + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": summer_camp_classes = summer_camp_classes.filter(is_public=True) context["summer_camp_classes"] = summer_camp_classes @@ -168,22 +145,14 @@ def get_board(self, context): # The correct order context["board"] = list( - chain( - roles["Chair"], - roles["Vice Chair"], - roles["Treasurer"], - roles["Secretary"], - roles["Director"], - ) + chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) ) return context # Associate Board Members def get_associate_board(self, context): - associate_board = AssociateBoardMember.objects.filter(is_active=True,).order_by( - "name" - ) + associate_board = AssociateBoardMember.objects.filter(is_active=True,).order_by("name") # NOTE: We're splitting and reordering them manually since the website requires a specific order. roles = defaultdict(list) @@ -193,30 +162,22 @@ def get_associate_board(self, context): # The correct order context["associate_board"] = list( - chain( - roles["Chair"], - roles["Vice Chair"], - roles["Treasurer"], - roles["Secretary"], - roles["Director"], - ) + chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) ) return context # Instructors def get_instructors(self, context, volunteers): - context["instructors"] = volunteers.filter( - user__groups__name__in=["Instructor"], - ).order_by("user__first_name") + context["instructors"] = volunteers.filter(user__groups__name__in=["Instructor"],).order_by("user__first_name") return context # Volunteers def get_volunteers(self, context, volunteers): - all_volunteers = volunteers.annotate( - session_count=Count("mentororder") - ).order_by("-user__role", "-session_count") + all_volunteers = volunteers.annotate(session_count=Count("mentororder")).order_by( + "-user__role", "-session_count" + ) mentors = [] volunteers = [] @@ -263,8 +224,7 @@ def form_valid(self, form): # It should return an HttpResponse. form.send_email() messages.success( - self.request, - "Thank you for contacting us! We will respond as soon as possible.", + self.request, "Thank you for contacting us! We will respond as soon as possible.", ) return super().form_valid(form) From d702216422f23cf93a6db883748fc1bd5d9a2b0c Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Mon, 28 Dec 2020 21:47:05 -0600 Subject: [PATCH 05/57] Clean up hard to read if-statements --- accounts/views.py | 5 ++++- coderdojochi/old_views.py | 15 ++++++++++++--- .../templatetags/coderdojochi_extras.py | 11 +++++++++-- coderdojochi/util.py | 5 ++++- coderdojochi/views/meetings.py | 11 +++++++++-- coderdojochi/views/welcome.py | 17 ++++++++++++++--- weallcode/templatetags/active.py | 5 ++++- 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/accounts/views.py b/accounts/views.py index 07520990..59008d31 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -59,7 +59,10 @@ def dispatch(self, *args, **kwargs): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["highlight"] = self.request.GET["highlight"] if "highlight" in self.request.GET else False + if "highlight" in self.request.GET: + context["highlight"] = self.request.GET["highlight"] + else: + context["highlight"] = False context["user"] = self.request.user diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 6a16653b..9f02c564 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -350,7 +350,10 @@ def session_check_in(request, pk, template_name="session-check-in.html"): session = get_object_or_404(Session, pk=pk) # Active Session - active_session = True if timezone.now() < session.end_date else False + if timezone.now() < session.end_date: + active_session = True + else: + active_session = False # get the orders orders = ( @@ -396,7 +399,10 @@ def session_check_in(request, pk, template_name="session-check-in.html"): # ) # Average Age - average_age = int(round(sum(ages) / float(len(ages)))) if orders and ages else 0 + if orders and ages: + average_age = int(round(sum(ages) / float(len(ages)))) + else: + average_age = 0 return render( request, @@ -439,7 +445,10 @@ def session_check_in_mentors(request, pk, template_name="session-check-in-mentor session = get_object_or_404(Session, pk=pk) # Active Session - active_session = True if timezone.now() < session.end_date else False + if timezone.now() < session.end_date: + active_session = True + else: + active_session = False # get the orders orders = MentorOrder.objects.select_related().filter(session_id=pk) diff --git a/coderdojochi/templatetags/coderdojochi_extras.py b/coderdojochi/templatetags/coderdojochi_extras.py index 0210afd9..fbb3e8ff 100644 --- a/coderdojochi/templatetags/coderdojochi_extras.py +++ b/coderdojochi/templatetags/coderdojochi_extras.py @@ -70,7 +70,11 @@ def student_register_link(context, student, session): ) elif not student.is_within_gender_limitation(session.gender_limitation): - title = "{gender}-only event.".format(gender="Girls" if session.gender_limitation == "female" else "Boys") + if session.gender_limitation == "female": + title = "Girls-only event." + else: + title = "Boys-only event." + message = f"Sorry, this class is limited to {session.gender_limitation}s this time around." button_href = ( 'data-trigger="hover" data-placement="top" data-toggle="popover" title="" ' @@ -98,4 +102,7 @@ def menu_is_active(context, pattern_or_urlname, css_class="active"): except NoReverseMatch: pattern = pattern_or_urlname - return css_class if re.search(pattern, context["request"].path) else "" + if re.search(pattern, context["request"].path): + return css_class + else: + return "" diff --git a/coderdojochi/util.py b/coderdojochi/util.py index c2dbf0e8..0da1087e 100644 --- a/coderdojochi/util.py +++ b/coderdojochi/util.py @@ -53,7 +53,10 @@ def email( final_merge_global_data = {} for key, val in merge_global_data.items(): if merge_field_format.format(key) in body: - final_merge_global_data[key] = "" if val is None else str(val) + if val is None: + final_merge_global_data[key] = "" + else: + final_merge_global_data[key] = str(val) esp_extra = { "merge_field_format": merge_field_format, diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index c26cac01..b8f6938b 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -82,7 +82,11 @@ class MeetingCalendarView(CalendarView): event_class = Meeting def get_summary(self, request, event_obj): - event_name = f"{event_obj.meeting_type.code} - " if event_obj.meeting_type.code else "" + if event_obj.meeting_type.code: + event_name = f"{event_obj.meeting_type.code} - " + else: + event_name = "" + event_name += event_obj.meeting_type.title return f"We All Code: {event_name}" @@ -107,7 +111,10 @@ def meeting_sign_up(request, pk, template_name="meeting-sign-up.html"): meeting_orders = MeetingOrder.objects.filter(meeting=meeting_obj, is_active=True) user_meeting_order = meeting_orders.filter(mentor=mentor) - user_signed_up = True if user_meeting_order.count() else False + if user_meeting_order.count(): + user_signed_up = True + else: + user_signed_up = False if request.method == "POST": diff --git a/coderdojochi/views/welcome.py b/coderdojochi/views/welcome.py index b02a2b1a..d0d08863 100644 --- a/coderdojochi/views/welcome.py +++ b/coderdojochi/views/welcome.py @@ -25,8 +25,13 @@ def dispatch(self, request, *args, **kwargs): # Check for redirect condition on mentor, otherwise pass as kwarg if getattr(request.user, "role", False) == "mentor" and request.method == "GET": mentor = get_object_or_404(Mentor, user=request.user) + if mentor.user.first_name: - return redirect(next_url if next_url else "account_home") + if next_url: + return redirect(next_url) + else: + return redirect("account_home") + kwargs["mentor"] = mentor return super().dispatch(request, *args, **kwargs) @@ -91,7 +96,10 @@ def update_account(self, request, account, next_url): if "enroll" in request.GET: next_url = f"{next_url}?enroll=True" else: - next_url = "account_home" if isinstance(account, Mentor) else "welcome" + if isinstance(account, Mentor): + next_url = "account_home" + else: + next_url = "welcome" return redirect(next_url) return render( @@ -135,7 +143,10 @@ def create_new_user(self, request, user, next_url): merge_global_data = {"user": user.username, "first_name": user.first_name, "last_name": user.last_name} - next_url = f"?next={next_url}" if next_url else None + if next_url: + next_url = f"?next={next_url}" + else: + next_url = None if role == "mentor": # check for next upcoming meeting diff --git a/weallcode/templatetags/active.py b/weallcode/templatetags/active.py index 004ee9cd..3a36621f 100644 --- a/weallcode/templatetags/active.py +++ b/weallcode/templatetags/active.py @@ -13,4 +13,7 @@ def active(context, pattern_or_urlname, css_class="active"): except NoReverseMatch: pattern = pattern_or_urlname - return css_class if re.search(pattern, context["request"].path) else "" + if re.search(pattern, context["request"].path): + return css_class + else: + return "" From 1d62953858377376e15e35be47c3a012fb281c8a Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 8 Jan 2021 11:55:09 -0600 Subject: [PATCH 06/57] Cleanup --- .vscode/settings.json | 6 +- coderdojochi/admin.py | 2 +- .../migrations/0037_auto_20201229_1510.py | 18 + coderdojochi/models.py | 874 ------------------ coderdojochi/models/__init__.py | 15 + coderdojochi/models/common.py | 12 + coderdojochi/models/course.py | 35 + coderdojochi/models/donation.py | 52 ++ coderdojochi/models/email_content.py | 21 + coderdojochi/models/equipment.py | 51 + coderdojochi/models/guardian.py | 36 + coderdojochi/models/location.py | 24 + coderdojochi/models/meeting.py | 99 ++ coderdojochi/models/mentor.py | 74 ++ coderdojochi/models/mentor_order.py | 37 + coderdojochi/models/order.py | 40 + coderdojochi/models/race_ethnicity.py | 15 + coderdojochi/models/session.py | 232 +++++ coderdojochi/models/student.py | 87 ++ coderdojochi/models/user.py | 32 + coderdojochi/old_views.py | 2 +- .../templates/guardian/session-detail.html | 231 +++-- coderdojochi/templates/meeting-detail.html | 151 ++- .../templates/public/session-detail.html | 125 +++ coderdojochi/templates/session-detail.html | 127 --- coderdojochi/urls.py | 15 +- coderdojochi/views/__init__.py | 5 + coderdojochi/views/guardian/__init__.py | 1 + coderdojochi/views/guardian/sessions.py | 22 + coderdojochi/views/mentor/__init__.py | 1 + coderdojochi/views/mentor/sessions.py | 17 + coderdojochi/views/public/__init__.py | 1 + coderdojochi/views/public/sessions.py | 15 + coderdojochi/views/sessions.py | 220 ++--- fixtures/17-weallcode.boardmember.json | 5 + .../18-weallcode.associateboardmember.json | 13 + fixtures/19-weallcode.staffmember.json | 1 + weallcode/admin.py | 4 + .../migrations/0007_auto_20201229_1542.py | 44 + weallcode/models/__init__.py | 4 + weallcode/models/associate_board_member.py | 16 + weallcode/models/board_member.py | 14 + weallcode/{models.py => models/common.py} | 47 +- weallcode/models/staff.py | 21 + weallcode/templates/weallcode/_base.html | 6 - weallcode/views.py | 380 ++++---- weallcode/views/__init__.py | 11 + weallcode/views/associate_board.py | 12 + weallcode/views/common.py | 43 + weallcode/views/credits.py | 11 + weallcode/views/home.py | 26 + weallcode/views/join_us.py | 26 + weallcode/views/our_story.py | 12 + weallcode/views/privacy.py | 11 + weallcode/views/programs.py | 40 + weallcode/views/programs_summer_camps.py | 26 + weallcode/views/sitemap.py | 27 + weallcode/views/team.py | 64 ++ 58 files changed, 2029 insertions(+), 1530 deletions(-) create mode 100644 coderdojochi/migrations/0037_auto_20201229_1510.py delete mode 100644 coderdojochi/models.py create mode 100644 coderdojochi/models/__init__.py create mode 100644 coderdojochi/models/common.py create mode 100644 coderdojochi/models/course.py create mode 100644 coderdojochi/models/donation.py create mode 100644 coderdojochi/models/email_content.py create mode 100644 coderdojochi/models/equipment.py create mode 100644 coderdojochi/models/guardian.py create mode 100644 coderdojochi/models/location.py create mode 100644 coderdojochi/models/meeting.py create mode 100644 coderdojochi/models/mentor.py create mode 100644 coderdojochi/models/mentor_order.py create mode 100644 coderdojochi/models/order.py create mode 100644 coderdojochi/models/race_ethnicity.py create mode 100644 coderdojochi/models/session.py create mode 100644 coderdojochi/models/student.py create mode 100644 coderdojochi/models/user.py create mode 100644 coderdojochi/templates/public/session-detail.html delete mode 100644 coderdojochi/templates/session-detail.html create mode 100644 coderdojochi/views/guardian/__init__.py create mode 100644 coderdojochi/views/guardian/sessions.py create mode 100644 coderdojochi/views/mentor/__init__.py create mode 100644 coderdojochi/views/mentor/sessions.py create mode 100644 coderdojochi/views/public/__init__.py create mode 100644 coderdojochi/views/public/sessions.py create mode 100644 weallcode/migrations/0007_auto_20201229_1542.py create mode 100644 weallcode/models/__init__.py create mode 100644 weallcode/models/associate_board_member.py create mode 100644 weallcode/models/board_member.py rename weallcode/{models.py => models/common.py} (60%) create mode 100644 weallcode/models/staff.py create mode 100644 weallcode/views/__init__.py create mode 100644 weallcode/views/associate_board.py create mode 100644 weallcode/views/common.py create mode 100644 weallcode/views/credits.py create mode 100644 weallcode/views/home.py create mode 100644 weallcode/views/join_us.py create mode 100644 weallcode/views/our_story.py create mode 100644 weallcode/views/privacy.py create mode 100644 weallcode/views/programs.py create mode 100644 weallcode/views/programs_summer_camps.py create mode 100644 weallcode/views/sitemap.py create mode 100644 weallcode/views/team.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 31a0b0a1..b3d09023 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,6 +41,7 @@ "--enable=W0614", // Used when an imported module or variable is not used from a 'from X import *' style import. "--load-plugins=pylint_django" ], + "python.autoComplete.extraPaths": ["./"], "[python]": { "editor.rulers": [120], "editor.codeActionsOnSave": { @@ -68,5 +69,8 @@ "editor.wordWrap": "on", "editor.quickSuggestions": false, "editor.trimAutoWhitespace": false - } + }, + "python.analysis.extraPaths": [ + "./" + ] } diff --git a/coderdojochi/admin.py b/coderdojochi/admin.py index ff6a606d..f1ea6d3f 100644 --- a/coderdojochi/admin.py +++ b/coderdojochi/admin.py @@ -12,7 +12,7 @@ from import_export.admin import ImportExportActionModelAdmin, ImportExportMixin from import_export.fields import Field -from coderdojochi.models import ( +from .models import ( Course, Donation, Equipment, diff --git a/coderdojochi/migrations/0037_auto_20201229_1510.py b/coderdojochi/migrations/0037_auto_20201229_1510.py new file mode 100644 index 00000000..f3445023 --- /dev/null +++ b/coderdojochi/migrations/0037_auto_20201229_1510.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.2 on 2020-12-29 21:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('coderdojochi', '0036_auto_20200812_1246'), + ] + + operations = [ + migrations.AlterField( + model_name='cdcuser', + name='first_name', + field=models.CharField(blank=True, max_length=150, verbose_name='first name'), + ), + ] diff --git a/coderdojochi/models.py b/coderdojochi/models.py deleted file mode 100644 index 7ff45757..00000000 --- a/coderdojochi/models.py +++ /dev/null @@ -1,874 +0,0 @@ -import os -from datetime import timedelta - -from django.contrib.auth.models import AbstractUser -from django.contrib.contenttypes.models import ContentType -from django.core.validators import MaxValueValidator, MinValueValidator -from django.db import models -from django.urls import reverse -from django.utils import formats, timezone -from django.utils.functional import cached_property - -from stdimage.models import StdImageField - -from .notifications import NewMentorBgCheckNotification, NewMentorNotification, NewMentorOrderNotification - - -class CDCUser(AbstractUser): - - MENTOR = "mentor" - GUARDIAN = "guardian" - - ROLE_CHOICES = [ - (MENTOR, "mentor"), - (GUARDIAN, "guardian"), - ] - - role = models.CharField(choices=ROLE_CHOICES, max_length=10, blank=True, null=True,) - - admin_notes = models.TextField(blank=True, null=True,) - - @cached_property - def name(self): - return f"{self.first_name} {self.last_name}" - - def save(self, *args, **kwargs): - if self.pk is None: - self.last_login = timezone.now() - super(CDCUser, self).save(*args, **kwargs) - - def get_absolute_url(self): - return reverse("account_home") - - -def generate_filename(instance, filename): - # file will be uploaded to MEDIA_ROOT/avatar/ - filename, file_extension = os.path.splitext(filename) - return f"avatar/{instance.user.username}{file_extension.lower()}" - - -class CommonInfo(models.Model): - # Auto create/update - created_at = models.DateTimeField(auto_now_add=True,) - - updated_at = models.DateTimeField(auto_now=True,) - - class Meta: - abstract = True - - -class RaceEthnicity(CommonInfo): - race_ethnicity = models.CharField(max_length=255,) - - is_visible = models.BooleanField(default=False,) - - class Meta: - verbose_name = "Race/Ethnicity" - verbose_name_plural = "Race/Ethnicities" - - def __str__(self): - return self.race_ethnicity - - -class Mentor(CommonInfo): - - user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - - bio = models.TextField(blank=True, null=True,) - - is_active = models.BooleanField(default=True,) - - background_check = models.BooleanField(default=False,) - - is_public = models.BooleanField(default=False,) - - avatar = StdImageField( - upload_to=generate_filename, blank=True, variations={"thumbnail": {"width": 500, "height": 500, "crop": True,}}, - ) - - avatar_approved = models.BooleanField(default=False,) - - birthday = models.DateTimeField(blank=False, null=True,) - - gender = models.CharField(max_length=255, blank=False, null=True,) - - race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=False,) - - work_place = models.CharField(max_length=255, blank=True, null=True,) - - phone = models.CharField(max_length=255, blank=True, null=True,) - - home_address = models.CharField(max_length=255, blank=True, null=True,) - - def __str__(self): - return f"{self.user.first_name} {self.user.last_name}" - - def full_name(self): - return f"{self.user.first_name} {self.user.last_name}" - - def save(self, *args, **kwargs): - if self.pk is None: - NewMentorNotification(self).send() - else: - orig = Mentor.objects.get(pk=self.pk) - if orig.avatar != self.avatar: - self.avatar_approved = False - - if self.background_check is True and orig.background_check != self.background_check: - NewMentorBgCheckNotification(self).send() - - super(Mentor, self).save(*args, **kwargs) - - def get_approve_avatar_url(self): - return reverse("mentor-approve-avatar", args=[str(self.id)]) - - def get_reject_avatar_url(self): - return reverse("mentor-reject-avatar", args=[str(self.id)]) - - def get_absolute_url(self): - return reverse("mentor-detail", args=[str(self.id)]) - - @property - def first_name(self): - return self.user.first_name - - @property - def last_name(self): - return self.user.last_name - - -class Guardian(CommonInfo): - user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - - is_active = models.BooleanField(default=True,) - - phone = models.CharField(max_length=50, blank=True,) - - zip = models.CharField(max_length=20, blank=True, null=True,) - - birthday = models.DateTimeField(blank=False, null=True,) - - gender = models.CharField(max_length=255, blank=False, null=True,) - - race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=False,) - - def __str__(self): - return f"{self.user.first_name} {self.user.last_name}" - - @property - def first_name(self): - return self.user.first_name - - @property - def last_name(self): - return self.user.last_name - - @property - def email(self): - return self.user.email - - def get_students(self): - return Student.objects.filter(guardian=self, is_active=True,) - - -class Student(CommonInfo): - guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE,) - - first_name = models.CharField(max_length=255,) - - last_name = models.CharField(max_length=255,) - - birthday = models.DateTimeField() - - gender = models.CharField(max_length=255,) - - race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=True,) - - school_name = models.CharField(max_length=255, null=True,) - - school_type = models.CharField(max_length=255, null=True,) - - medical_conditions = models.TextField(blank=True, null=True,) - - medications = models.TextField(blank=True, null=True,) - - photo_release = models.BooleanField( - "Photo Consent", - help_text=( - "I hereby give permission to We All Code to use " - "the student's image and/or likeness in promotional materials." - ), - default=False, - ) - - consent = models.BooleanField( - "General Consent", - help_text=("I hereby give consent for the student signed up " "above to participate in We All Code."), - default=False, - ) - - is_active = models.BooleanField(default=True,) - - def __str__(self): - return f"{self.first_name} {self.last_name}" - - def is_registered_for_session(self, session): - try: - Order.objects.get( - is_active=True, student=self, session=session, - ) - is_registered = True - except Exception: - is_registered = False - - return is_registered - - def get_age(self, date=timezone.now()): - return date.year - self.birthday.year - ((date.month, date.day) < (self.birthday.month, self.birthday.day)) - - get_age.short_description = "Age" - - def get_clean_gender(self): - MALE = ["male", "m", "boy", "nino", "masculino"] - FEMALE = ["female", "f", "girl", "femail", "femal", "femenino"] - - if self.gender.lower() in MALE: - return "male" - elif self.gender.lower() in FEMALE: - return "female" - else: - return "other" - - get_clean_gender.short_description = "Clean Gender" - - # returns True if the student age is between minimum_age and maximum_age - def is_within_age_range(self, minimum_age, maximum_age, date=timezone.now()): - age = self.get_age(date) - - if age >= minimum_age and age <= maximum_age: - return True - else: - return False - - def is_within_gender_limitation(self, limitation): - if limitation: - if self.get_clean_gender() in [limitation.lower(), "other"]: - return True - else: - return False - else: - return True - - -class Course(CommonInfo): - WEEKEND = "WE" - CAMP = "CA" - SPECIAL = "SP" - - COURSE_TYPE_CHOICES = [ - (WEEKEND, "Weekend"), - (CAMP, "Camp"), - (SPECIAL, "Special"), - ] - - code = models.CharField(max_length=255, blank=True, null=True,) - - course_type = models.CharField("type", max_length=2, choices=COURSE_TYPE_CHOICES, default=WEEKEND,) - - title = models.CharField(max_length=255,) - - slug = models.SlugField(max_length=40, blank=True, null=True,) - - description = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - - duration = models.DurationField(default=timedelta(hours=3), help_text="HH:MM:ss",) - - minimum_age = models.IntegerField(default=7, validators=[MinValueValidator(0), MaxValueValidator(100)],) - - maximum_age = models.IntegerField(default=17, validators=[MinValueValidator(0), MaxValueValidator(100)],) - - is_active = models.BooleanField(default=True,) - - def __str__(self): - if self.code: - return f"{self.code} | {self.title}" - else: - return f"{self.title}" - - -class Location(CommonInfo): - - name = models.CharField(max_length=255,) - - address = models.CharField(blank=True, null=True, max_length=255,) - - city = models.CharField(blank=True, null=True, max_length=255,) - - state = models.CharField(blank=True, null=True, max_length=2,) - - zip = models.CharField(blank=True, null=True, max_length=20,) - - is_active = models.BooleanField(default=True,) - - class Meta: - ordering = ["-id"] - - def __str__(self): - return self.name - - -class Session(CommonInfo): - MALE = "male" - FEMALE = "female" - - GENDER_LIMITATION_CHOICES = ( - (MALE, "Male"), - (FEMALE, "Female"), - ) - - course = models.ForeignKey(Course, on_delete=models.CASCADE, limit_choices_to={"is_active": True},) - - start_date = models.DateTimeField() - - location = models.ForeignKey(Location, on_delete=models.CASCADE, limit_choices_to={"is_active": True},) - - capacity = models.IntegerField(default=20,) - - mentor_capacity = models.IntegerField(blank=True, null=True,) - - instructor = models.ForeignKey( - Mentor, - on_delete=models.CASCADE, - related_name="session_instructor", - limit_choices_to={"user__groups__name": "Instructor"}, - ) - - # Pricing - cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) - - minimum_cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) - - maximum_cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) - - additional_info = models.TextField(blank=True, null=True, help_text="Basic HTML allowed") - - waitlist_mentors = models.ManyToManyField(Mentor, blank=True, related_name="session_waitlist_mentors",) - - waitlist_students = models.ManyToManyField(Student, blank=True, related_name="session_waitlist_students",) - - external_enrollment_url = models.CharField( - max_length=255, blank=True, null=True, help_text="When provided, local enrollment is disabled.", - ) - - is_active = models.BooleanField(default=False, help_text="Session is active.",) - - is_public = models.BooleanField(default=False, help_text="Session is a public session.",) - - password = models.CharField(blank=True, max_length=255,) - - partner_message = models.TextField(blank=True,) - - announced_date_mentors = models.DateTimeField(blank=True, null=True,) - - announced_date_guardians = models.DateTimeField(blank=True, null=True,) - - image_url = models.CharField(max_length=255, blank=True, null=True,) - - bg_image = models.ImageField(blank=True, null=True,) - - mentors_week_reminder_sent = models.BooleanField(default=False,) - - mentors_day_reminder_sent = models.BooleanField(default=False,) - - gender_limitation = models.CharField( - help_text="Limits the class to be only one gender.", - max_length=255, - choices=GENDER_LIMITATION_CHOICES, - blank=True, - null=True, - ) - - override_minimum_age_limitation = models.IntegerField( - "Min Age", - help_text="Only update this if different from the default.", - blank=True, - null=True, - validators=[MinValueValidator(0), MaxValueValidator(100)], - ) - - override_maximum_age_limitation = models.IntegerField( - "Max Age", - help_text="Only update this if different from the default.", - blank=True, - null=True, - validators=[MinValueValidator(0), MaxValueValidator(100)], - ) - - online_video_link = models.URLField( - "Online Video Link", help_text="Zoom link with password.", blank=True, null=True, - ) - - online_video_meeting_id = models.CharField( - "Online Video Meeting ID", help_text="XXX XXXX XXXX", max_length=255, blank=True, null=True, - ) - - online_video_meeting_password = models.CharField( - "Online Video Meeting Password", - help_text="Plain text password shared by Zoom", - max_length=255, - blank=True, - null=True, - ) - - online_video_description = models.TextField( - "Online Video Description", - help_text="Information on how to connect to the video call. Basic HTML allowed.", - blank=True, - null=True, - ) - - # kept for older records - old_end_date = models.DateTimeField(blank=True, null=True,) - old_mentor_start_date = models.DateTimeField(blank=True, null=True,) - old_mentor_end_date = models.DateTimeField(blank=True, null=True,) - - def __str__(self): - date = formats.date_format(self.start_date, "SHORT_DATETIME_FORMAT") - return f"{self.course.title} | {date}" - - def save(self, *args, **kwargs): - if self.mentor_capacity is None: - self.mentor_capacity = int(self.capacity / 2) - - super(Session, self).save(*args, **kwargs) - - def get_absolute_url(self): - return reverse("session-detail", args=[str(self.id)]) - - def get_sign_up_url(self): - return reverse("session-sign-up", args=[str(self.id)]) - - def get_calendar_url(self): - return reverse("session-calendar", args=[str(self.id)]) - - def get_current_orders(self, checked_in=None): - if checked_in is not None: - if checked_in: - orders = ( - Order.objects.filter(is_active=True, session=self) - .exclude(check_in=None) - .order_by("student__last_name") - ) - else: - orders = Order.objects.filter(is_active=True, session=self, check_in=None).order_by( - "student__last_name" - ) - else: - orders = Order.objects.filter(is_active=True, session=self).order_by("check_in", "student__last_name") - - return orders - - def get_mentor_orders(self): - orders = MentorOrder.objects.filter(session=self, is_active=True,).order_by("mentor__user__last_name") - - return orders - - def get_checked_in_mentor_orders(self): - orders = MentorOrder.objects.filter(session=self, is_active=True, check_in__isnull=False).order_by( - "mentor__user__last_name" - ) - return orders - - def get_current_students(self, checked_in=None): - if checked_in is not None: - if checked_in: - orders = Order.objects.filter(is_active=True, session=self).exclude(check_in=None).values("student") - else: - orders = Order.objects.filter(is_active=True, session=self, check_in=None).values("student") - else: - orders = Order.objects.filter(is_active=True, session=self).values("student") - - return orders - - def get_checked_in_students(self): - return Order.objects.filter(is_active=True, session=self).exclude(check_in=None).values("student") - - def get_mentor_capacity(self): - if self.mentor_capacity: - return self.mentor_capacity - else: - return int(self.capacity / 2) - - def is_guardian_announced(self): - return self.announced_date_guardians is not None - - is_guardian_announced.boolean = True - is_guardian_announced.short_description = "Is Announced" - is_guardian_announced.admin_order_field = "announced_date_guardians" - - @property - def end_date(self): - # Some records have a defined record with the end date, - # rather than use the course's duration. - # We're keeping this for old records. - if self.old_end_date: - return self.old_end_date - - return self.start_date + self.course.duration - - @property - def mentor_start_date(self): - # Some records have a defined record with the mentor start date, - # rather than do the math. - # We're keeping this for old records. - if self.old_mentor_start_date: - return self.old_mentor_start_date - - return self.start_date - timedelta(hours=1) - - @property - def mentor_end_date(self): - # Some records have a defined record with the mentor start date, - # rather than do the math. - # We're keeping this for old records. - if self.old_mentor_end_date: - return self.old_mentor_end_date - - return self.end_date + timedelta(hours=1) - - @property - def minimum_age(self): - if self.override_minimum_age_limitation is not None: - return self.override_minimum_age_limitation - - return self.course.minimum_age - - @property - def maximum_age(self): - if self.override_maximum_age_limitation is not None: - return self.override_maximum_age_limitation - - return self.course.maximum_age - - -class MeetingType(CommonInfo): - - code = models.CharField(max_length=255, blank=True, null=True,) - - title = models.CharField(max_length=255,) - - slug = models.SlugField(max_length=40, blank=True, null=True,) - - description = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - - def __str__(self): - if self.code: - return f"{self.code} | {self.title}" - else: - return f"{self.title}" - - -class Meeting(CommonInfo): - - meeting_type = models.ForeignKey(MeetingType, on_delete=models.CASCADE,) - - additional_info = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - - start_date = models.DateTimeField(blank=True, null=True,) - - end_date = models.DateTimeField(blank=True, null=True,) - - location = models.ForeignKey(Location, on_delete=models.CASCADE,) - - external_enrollment_url = models.CharField( - max_length=255, blank=True, null=True, help_text="When provided, local enrollment is disabled.", - ) - - is_public = models.BooleanField(default=False,) - - is_active = models.BooleanField(default=False,) - - image_url = models.CharField(max_length=255, blank=True, null=True,) - - bg_image = models.ImageField(blank=True, null=True,) - - announced_date = models.DateTimeField(blank=True, null=True,) - - def __str__(self): - date = formats.date_format(self.start_date, "SHORT_DATETIME_FORMAT") - return f"{self.meeting_type.title} | {date}" - - def get_absolute_url(self): - return reverse("meeting-detail", args=[str(self.id)]) - - def get_sign_up_url(self): - return reverse("meeting-register", args=[str(self.id)]) - - def get_calendar_url(self): - return reverse("meeting-calendar", args=[str(self.id)]) - - def get_current_orders(self, checked_in=None): - if checked_in is not None: - if checked_in: - orders = ( - MeetingOrder.objects.filter(is_active=True, meeting=self,) - .exclude(check_in=None,) - .order_by("mentor__user__last_name") - ) - else: - orders = MeetingOrder.objects.filter(is_active=True, meeting=self, check_in=None,).order_by( - "mentor__user__last_name" - ) - - else: - orders = MeetingOrder.objects.filter(is_active=True, meeting=self,).order_by( - "check_in", "mentor__user__last_name", - ) - - return orders - - def get_current_mentors(self): - return Mentor.objects.filter( - id__in=MeetingOrder.objects.filter(is_active=True, meeting=self,).values("mentor__id",) - ) - - def get_mentor_count(self): - return MeetingOrder.objects.filter(meeting__id=self.id).count() - - get_mentor_count.short_description = "Mentors" - - -class Order(CommonInfo): - - guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE,) - - session = models.ForeignKey(Session, on_delete=models.CASCADE,) - - student = models.ForeignKey(Student, on_delete=models.CASCADE,) - - is_active = models.BooleanField(default=True,) - - ip = models.CharField(max_length=255, blank=True, null=True,) - - check_in = models.DateTimeField(blank=True, null=True,) - - alternate_guardian = models.CharField(max_length=255, blank=True, null=True,) - - affiliate = models.CharField(max_length=255, blank=True, null=True,) - - order_number = models.CharField(max_length=255, blank=True, null=True,) - - week_reminder_sent = models.BooleanField(default=False,) - - day_reminder_sent = models.BooleanField(default=False,) - - def __str__(self): - return f"{self.student.first_name} {self.student.last_name} | {self.session.course.title}" - - def is_checked_in(self): - return self.check_in is not None - - is_checked_in.boolean = True - - def get_student_age(self): - return self.student.get_age(self.session.start_date) - - get_student_age.short_description = "Age" - - def get_student_gender(self): - return self.student.get_clean_gender().title() - - get_student_gender.short_description = "Gender" - - -class MentorOrder(CommonInfo): - mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE,) - - session = models.ForeignKey(Session, on_delete=models.CASCADE,) - - is_active = models.BooleanField(default=True,) - - ip = models.CharField(max_length=255, blank=True, null=True,) - - check_in = models.DateTimeField(blank=True, null=True,) - - affiliate = models.CharField(max_length=255, blank=True, null=True,) - - order_number = models.CharField(max_length=255, blank=True, null=True,) - - week_reminder_sent = models.BooleanField(default=False,) - - day_reminder_sent = models.BooleanField(default=False,) - - def __str__(self): - return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.session.course.title}" - - def is_checked_in(self): - return self.check_in is not None - - is_checked_in.boolean = True - - def save(self, *args, **kwargs): - num_orders = MentorOrder.objects.filter(mentor__id=self.mentor.id).count() - - if self.pk is None and num_orders == 0: - NewMentorOrderNotification(self).send() - - super(MentorOrder, self).save(*args, **kwargs) - - -class MeetingOrder(CommonInfo): - - mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE,) - - meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE,) - - is_active = models.BooleanField(default=True,) - - ip = models.CharField(max_length=255, blank=True, null=True,) - - check_in = models.DateTimeField(blank=True, null=True,) - - affiliate = models.CharField(max_length=255, blank=True, null=True,) - - order_number = models.CharField(max_length=255, blank=True, null=True,) - - week_reminder_sent = models.BooleanField(default=False,) - - day_reminder_sent = models.BooleanField(default=False,) - - def __str__(self): - return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.meeting.meeting_type.title}" - - def is_checked_in(self): - return self.check_in is not None - - is_checked_in.boolean = True - - -class EquipmentType(CommonInfo): - - name = models.CharField(max_length=255, blank=False, null=False,) - - def __str__(self): - return self.name - - -class Equipment(CommonInfo): - WORKING = "working" - ISSUE = "issue" - UNUSABLE = "unusable" - EQUIPMENT_CONDITIONS = [ - (WORKING, "Working"), - (ISSUE, "Issue"), - (UNUSABLE, "Unusable"), - ] - - uuid = models.CharField(max_length=255, verbose_name="UUID", default="000-000-000-000", null=False,) - - equipment_type = models.ForeignKey(EquipmentType, on_delete=models.CASCADE,) - - make = models.CharField(max_length=255,) - - model = models.CharField(max_length=255,) - - asset_tag = models.CharField(max_length=255,) - - acquisition_date = models.DateTimeField(blank=True, null=True,) - - condition = models.CharField(max_length=255, choices=EQUIPMENT_CONDITIONS,) - - notes = models.TextField(blank=True, null=True,) - - last_system_update_check_in = models.DateTimeField(blank=True, null=True, verbose_name="Last Check In",) - - last_system_update = models.DateTimeField(blank=True, null=True, verbose_name="Last Update",) - - force_update_on_next_boot = models.BooleanField(default=False, verbose_name="Force Update",) - - class Meta: - verbose_name = "equipment" - verbose_name_plural = "equipment" - - def __str__(self): - return f"{self.equipment_type.name} | {self.make} {self.model} | {self.acquisition_date}" - - -class EmailContent(CommonInfo): - - nickname = models.CharField(max_length=255,) - - subject = models.CharField(max_length=255,) - - body = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - - is_active = models.BooleanField(default=True,) - - class Meta: - verbose_name = "email content" - verbose_name_plural = "email content" - - def __str__(self): - return f"{self.nickname} | {self.subject}" - - -class Donation(CommonInfo): - - user = models.ForeignKey(CDCUser, blank=True, null=True, on_delete=models.CASCADE,) - - session = models.ForeignKey(Session, blank=True, null=True, on_delete=models.CASCADE,) - - first_name = models.CharField(max_length=255, blank=True, null=True,) - - last_name = models.CharField(max_length=255, blank=True, null=True,) - - referral_code = models.CharField(max_length=255, blank=True, null=True,) - - email = models.EmailField(blank=True, null=True,) - - amount = models.IntegerField() - - is_verified = models.BooleanField(default=False,) - - receipt_sent = models.BooleanField(default=False,) - - def __str__(self): - return f"{self.email} | ${self.amount}" - - def get_admin_url(self): - content_type = ContentType.objects.get_for_model(self.__class__) - return reverse(f"admin:{content_type.app_label}_{content_type.model}_change", args=(self.id,),) - - def get_first_name(self): - if self.user: - return self.user.first_name - else: - return self.first_name - - get_first_name.short_description = "First Name" - - def get_last_name(self): - if self.user: - return self.user.last_name - else: - return self.last_name - - get_last_name.short_description = "Last Name" - - def get_email(self): - if self.user: - return self.user.email - else: - return self.email - - get_email.short_description = "Email" - - -class PartnerPasswordAccess(CommonInfo): - - user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - - session = models.ForeignKey(Session, on_delete=models.CASCADE,) - - class Meta: - db_table = "partner_password_access" diff --git a/coderdojochi/models/__init__.py b/coderdojochi/models/__init__.py new file mode 100644 index 00000000..3e23cd59 --- /dev/null +++ b/coderdojochi/models/__init__.py @@ -0,0 +1,15 @@ +from .common import * +from .course import * +from .donation import * +from .email_content import * +from .equipment import * +from .guardian import * +from .location import * +from .meeting import * +from .mentor import * +from .mentor_order import * +from .order import * +from .race_ethnicity import * +from .session import * +from .student import * +from .user import * diff --git a/coderdojochi/models/common.py b/coderdojochi/models/common.py new file mode 100644 index 00000000..3f561051 --- /dev/null +++ b/coderdojochi/models/common.py @@ -0,0 +1,12 @@ +from django.db import models + + +class CommonInfo(models.Model): + # Auto create/update + created_at = models.DateTimeField(auto_now_add=True,) + + updated_at = models.DateTimeField(auto_now=True,) + + class Meta: + abstract = True + diff --git a/coderdojochi/models/course.py b/coderdojochi/models/course.py new file mode 100644 index 00000000..f4a32b46 --- /dev/null +++ b/coderdojochi/models/course.py @@ -0,0 +1,35 @@ +from datetime import timedelta + +from django.core.validators import MaxValueValidator, MinValueValidator +from django.db import models + +from .common import CommonInfo + + +class Course(CommonInfo): + + WEEKEND = "WE" + CAMP = "CA" + SPECIAL = "SP" + + COURSE_TYPE_CHOICES = [ + (WEEKEND, "Weekend"), + (CAMP, "Camp"), + (SPECIAL, "Special"), + ] + + code = models.CharField(max_length=255, blank=True, null=True,) + course_type = models.CharField("type", max_length=2, choices=COURSE_TYPE_CHOICES, default=WEEKEND,) + title = models.CharField(max_length=255,) + slug = models.SlugField(max_length=40, blank=True, null=True,) + description = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) + duration = models.DurationField(default=timedelta(hours=3), help_text="HH:MM:ss",) + minimum_age = models.IntegerField(default=7, validators=[MinValueValidator(0), MaxValueValidator(100)],) + maximum_age = models.IntegerField(default=17, validators=[MinValueValidator(0), MaxValueValidator(100)],) + is_active = models.BooleanField(default=True,) + + def __str__(self): + if self.code: + return f"{self.code} | {self.title}" + else: + return f"{self.title}" diff --git a/coderdojochi/models/donation.py b/coderdojochi/models/donation.py new file mode 100644 index 00000000..173791ec --- /dev/null +++ b/coderdojochi/models/donation.py @@ -0,0 +1,52 @@ +from django.contrib.contenttypes.models import ContentType +from django.db import models +from django.urls import reverse + +from .common import CommonInfo + + +class Donation(CommonInfo): + from .session import Session + from .user import CDCUser + + user = models.ForeignKey(CDCUser, blank=True, null=True, on_delete=models.CASCADE,) + session = models.ForeignKey(Session, blank=True, null=True, on_delete=models.CASCADE,) + first_name = models.CharField(max_length=255, blank=True, null=True,) + last_name = models.CharField(max_length=255, blank=True, null=True,) + referral_code = models.CharField(max_length=255, blank=True, null=True,) + email = models.EmailField(blank=True, null=True,) + amount = models.IntegerField() + is_verified = models.BooleanField(default=False,) + receipt_sent = models.BooleanField(default=False,) + + def __str__(self): + return f"{self.email} | ${self.amount}" + + def get_admin_url(self): + content_type = ContentType.objects.get_for_model(self.__class__) + return reverse(f"admin:{content_type.app_label}_{content_type.model}_change", args=(self.id,),) + + def get_first_name(self): + if self.user: + return self.user.first_name + else: + return self.first_name + + get_first_name.short_description = "First Name" + + def get_last_name(self): + if self.user: + return self.user.last_name + else: + return self.last_name + + get_last_name.short_description = "Last Name" + + def get_email(self): + if self.user: + return self.user.email + else: + return self.email + + get_email.short_description = "Email" + diff --git a/coderdojochi/models/email_content.py b/coderdojochi/models/email_content.py new file mode 100644 index 00000000..ff59f457 --- /dev/null +++ b/coderdojochi/models/email_content.py @@ -0,0 +1,21 @@ +from django.db import models + +from .common import CommonInfo + + +class EmailContent(CommonInfo): + + nickname = models.CharField(max_length=255,) + + subject = models.CharField(max_length=255,) + + body = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) + + is_active = models.BooleanField(default=True,) + + class Meta: + verbose_name = "email content" + verbose_name_plural = "email content" + + def __str__(self): + return f"{self.nickname} | {self.subject}" diff --git a/coderdojochi/models/equipment.py b/coderdojochi/models/equipment.py new file mode 100644 index 00000000..397ea573 --- /dev/null +++ b/coderdojochi/models/equipment.py @@ -0,0 +1,51 @@ +from django.db import models + +from .common import CommonInfo + + +class EquipmentType(CommonInfo): + + name = models.CharField(max_length=255, blank=False, null=False,) + + def __str__(self): + return self.name + + +class Equipment(CommonInfo): + WORKING = "working" + ISSUE = "issue" + UNUSABLE = "unusable" + EQUIPMENT_CONDITIONS = [ + (WORKING, "Working"), + (ISSUE, "Issue"), + (UNUSABLE, "Unusable"), + ] + + uuid = models.CharField(max_length=255, verbose_name="UUID", default="000-000-000-000", null=False,) + + equipment_type = models.ForeignKey(EquipmentType, on_delete=models.CASCADE,) + + make = models.CharField(max_length=255,) + + model = models.CharField(max_length=255,) + + asset_tag = models.CharField(max_length=255,) + + acquisition_date = models.DateTimeField(blank=True, null=True,) + + condition = models.CharField(max_length=255, choices=EQUIPMENT_CONDITIONS,) + + notes = models.TextField(blank=True, null=True,) + + last_system_update_check_in = models.DateTimeField(blank=True, null=True, verbose_name="Last Check In",) + + last_system_update = models.DateTimeField(blank=True, null=True, verbose_name="Last Update",) + + force_update_on_next_boot = models.BooleanField(default=False, verbose_name="Force Update",) + + class Meta: + verbose_name = "equipment" + verbose_name_plural = "equipment" + + def __str__(self): + return f"{self.equipment_type.name} | {self.make} {self.model} | {self.acquisition_date}" diff --git a/coderdojochi/models/guardian.py b/coderdojochi/models/guardian.py new file mode 100644 index 00000000..4d1b4683 --- /dev/null +++ b/coderdojochi/models/guardian.py @@ -0,0 +1,36 @@ +from django.db import models + +from .common import CommonInfo +from .race_ethnicity import RaceEthnicity +from .user import CDCUser + + +class Guardian(CommonInfo): + user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) + is_active = models.BooleanField(default=True,) + phone = models.CharField(max_length=50, blank=True,) + zip = models.CharField(max_length=20, blank=True, null=True,) + birthday = models.DateTimeField(blank=False, null=True,) + gender = models.CharField(max_length=255, blank=False, null=True,) + race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=False,) + + def __str__(self): + return f"{self.user.first_name} {self.user.last_name}" + + @property + def first_name(self): + return self.user.first_name + + @property + def last_name(self): + return self.user.last_name + + @property + def email(self): + return self.user.email + + def get_students(self): + from .student import Student + + return Student.objects.filter(guardian=self, is_active=True,) + diff --git a/coderdojochi/models/location.py b/coderdojochi/models/location.py new file mode 100644 index 00000000..905f6e0b --- /dev/null +++ b/coderdojochi/models/location.py @@ -0,0 +1,24 @@ +from django.db import models + +from .common import CommonInfo + + +class Location(CommonInfo): + + name = models.CharField(max_length=255,) + + address = models.CharField(blank=True, null=True, max_length=255,) + + city = models.CharField(blank=True, null=True, max_length=255,) + + state = models.CharField(blank=True, null=True, max_length=2,) + + zip = models.CharField(blank=True, null=True, max_length=20,) + + is_active = models.BooleanField(default=True,) + + class Meta: + ordering = ["-id"] + + def __str__(self): + return self.name diff --git a/coderdojochi/models/meeting.py b/coderdojochi/models/meeting.py new file mode 100644 index 00000000..4cadc09d --- /dev/null +++ b/coderdojochi/models/meeting.py @@ -0,0 +1,99 @@ +from django.db import models +from django.urls import reverse +from django.utils import formats + +from .common import CommonInfo +from .location import Location +from .mentor import Mentor + + +class MeetingType(CommonInfo): + code = models.CharField(max_length=255, blank=True, null=True,) + title = models.CharField(max_length=255,) + slug = models.SlugField(max_length=40, blank=True, null=True,) + description = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) + + def __str__(self): + if self.code: + return f"{self.code} | {self.title}" + else: + return f"{self.title}" + + +class Meeting(CommonInfo): + meeting_type = models.ForeignKey(MeetingType, on_delete=models.CASCADE,) + additional_info = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) + start_date = models.DateTimeField(blank=True, null=True,) + end_date = models.DateTimeField(blank=True, null=True,) + location = models.ForeignKey(Location, on_delete=models.CASCADE,) + external_enrollment_url = models.CharField( + max_length=255, blank=True, null=True, help_text="When provided, local enrollment is disabled.", + ) + is_public = models.BooleanField(default=False,) + is_active = models.BooleanField(default=False,) + image_url = models.CharField(max_length=255, blank=True, null=True,) + bg_image = models.ImageField(blank=True, null=True,) + announced_date = models.DateTimeField(blank=True, null=True,) + + def __str__(self): + date = formats.date_format(self.start_date, "SHORT_DATETIME_FORMAT") + return f"{self.meeting_type.title} | {date}" + + def get_absolute_url(self): + return reverse("meeting-detail", args=[str(self.id)]) + + def get_sign_up_url(self): + return reverse("meeting-register", args=[str(self.id)]) + + def get_calendar_url(self): + return reverse("meeting-calendar", args=[str(self.id)]) + + def get_current_orders(self, checked_in=None): + if checked_in is not None: + if checked_in: + orders = ( + MeetingOrder.objects.filter(is_active=True, meeting=self,) + .exclude(check_in=None,) + .order_by("mentor__user__last_name") + ) + else: + orders = MeetingOrder.objects.filter(is_active=True, meeting=self, check_in=None,).order_by( + "mentor__user__last_name" + ) + + else: + orders = MeetingOrder.objects.filter(is_active=True, meeting=self,).order_by( + "check_in", "mentor__user__last_name", + ) + + return orders + + def get_current_mentors(self): + return Mentor.objects.filter( + id__in=MeetingOrder.objects.filter(is_active=True, meeting=self,).values("mentor__id",) + ) + + def get_mentor_count(self): + return MeetingOrder.objects.filter(meeting__id=self.id).count() + + get_mentor_count.short_description = "Mentors" + + +class MeetingOrder(CommonInfo): + mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE,) + meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE,) + is_active = models.BooleanField(default=True,) + ip = models.CharField(max_length=255, blank=True, null=True,) + check_in = models.DateTimeField(blank=True, null=True,) + affiliate = models.CharField(max_length=255, blank=True, null=True,) + order_number = models.CharField(max_length=255, blank=True, null=True,) + week_reminder_sent = models.BooleanField(default=False,) + day_reminder_sent = models.BooleanField(default=False,) + + def __str__(self): + return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.meeting.meeting_type.title}" + + def is_checked_in(self): + return self.check_in is not None + + is_checked_in.boolean = True diff --git a/coderdojochi/models/mentor.py b/coderdojochi/models/mentor.py new file mode 100644 index 00000000..cf7ce632 --- /dev/null +++ b/coderdojochi/models/mentor.py @@ -0,0 +1,74 @@ +import os + +from django.db import models +from django.urls import reverse + +from stdimage.models import StdImageField + +from ..notifications import NewMentorBgCheckNotification, NewMentorNotification, NewMentorOrderNotification +from .common import CommonInfo +from .race_ethnicity import RaceEthnicity +from .user import CDCUser + + +def generate_filename(instance, filename): + # file will be uploaded to MEDIA_ROOT/avatar/ + filename, file_extension = os.path.splitext(filename) + return f"avatar/{instance.user.username}{file_extension.lower()}" + + +# TODO: Add MentorManager +class Mentor(CommonInfo): + user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) + bio = models.TextField(blank=True, null=True,) + is_active = models.BooleanField(default=True,) + background_check = models.BooleanField(default=False,) + is_public = models.BooleanField(default=False,) + avatar = StdImageField( + upload_to=generate_filename, blank=True, variations={"thumbnail": {"width": 500, "height": 500, "crop": True,}}, + ) + avatar_approved = models.BooleanField(default=False,) + birthday = models.DateTimeField(blank=False, null=True,) + gender = models.CharField(max_length=255, blank=False, null=True,) + race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=False,) + work_place = models.CharField(max_length=255, blank=True, null=True,) + phone = models.CharField(max_length=255, blank=True, null=True,) + home_address = models.CharField(max_length=255, blank=True, null=True,) + + def __str__(self): + return f"{self.user.first_name} {self.user.last_name}" + + def full_name(self): + return f"{self.user.first_name} {self.user.last_name}" + + def save(self, *args, **kwargs): + if self.pk is None: + NewMentorNotification(self).send() + else: + orig = Mentor.objects.get(pk=self.pk) + if orig.avatar != self.avatar: + self.avatar_approved = False + + if self.background_check is True and orig.background_check != self.background_check: + NewMentorBgCheckNotification(self).send() + + super(Mentor, self).save(*args, **kwargs) + + def get_approve_avatar_url(self): + return reverse("mentor-approve-avatar", args=[str(self.id)]) + + def get_reject_avatar_url(self): + return reverse("mentor-reject-avatar", args=[str(self.id)]) + + def get_absolute_url(self): + return reverse("mentor-detail", args=[str(self.id)]) + + @property + def first_name(self): + return self.user.first_name + + @property + def last_name(self): + return self.user.last_name + + diff --git a/coderdojochi/models/mentor_order.py b/coderdojochi/models/mentor_order.py new file mode 100644 index 00000000..1fe0494d --- /dev/null +++ b/coderdojochi/models/mentor_order.py @@ -0,0 +1,37 @@ +import os + +from django.db import models + +from ..notifications import NewMentorOrderNotification +from .common import CommonInfo + + +class MentorOrder(CommonInfo): + from .mentor import Mentor + from .session import Session + + mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE,) + session = models.ForeignKey(Session, on_delete=models.CASCADE,) + is_active = models.BooleanField(default=True,) + ip = models.CharField(max_length=255, blank=True, null=True,) + check_in = models.DateTimeField(blank=True, null=True,) + affiliate = models.CharField(max_length=255, blank=True, null=True,) + order_number = models.CharField(max_length=255, blank=True, null=True,) + week_reminder_sent = models.BooleanField(default=False,) + day_reminder_sent = models.BooleanField(default=False,) + + def __str__(self): + return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.session.course.title}" + + def is_checked_in(self): + return self.check_in is not None + + is_checked_in.boolean = True + + def save(self, *args, **kwargs): + num_orders = self.objects.filter(mentor__id=self.mentor.id).count() + + if self.pk is None and num_orders == 0: + NewMentorOrderNotification(self).send() + + super().save(*args, **kwargs) diff --git a/coderdojochi/models/order.py b/coderdojochi/models/order.py new file mode 100644 index 00000000..629e5959 --- /dev/null +++ b/coderdojochi/models/order.py @@ -0,0 +1,40 @@ +from django.db import models + +from .common import CommonInfo + + +class Order(CommonInfo): + from .guardian import Guardian + from .session import Session + from .student import Student + + guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE,) + session = models.ForeignKey(Session, on_delete=models.CASCADE,) + student = models.ForeignKey(Student, on_delete=models.CASCADE,) + is_active = models.BooleanField(default=True,) + ip = models.CharField(max_length=255, blank=True, null=True,) + check_in = models.DateTimeField(blank=True, null=True,) + alternate_guardian = models.CharField(max_length=255, blank=True, null=True,) + affiliate = models.CharField(max_length=255, blank=True, null=True,) + order_number = models.CharField(max_length=255, blank=True, null=True,) + week_reminder_sent = models.BooleanField(default=False,) + day_reminder_sent = models.BooleanField(default=False,) + + def __str__(self): + return f"{self.student.first_name} {self.student.last_name} | {self.session.course.title}" + + def is_checked_in(self): + return self.check_in is not None + + is_checked_in.boolean = True + + def get_student_age(self): + return self.student.get_age(self.session.start_date) + + get_student_age.short_description = "Age" + + def get_student_gender(self): + return self.student.get_clean_gender().title() + + get_student_gender.short_description = "Gender" + diff --git a/coderdojochi/models/race_ethnicity.py b/coderdojochi/models/race_ethnicity.py new file mode 100644 index 00000000..3ca96352 --- /dev/null +++ b/coderdojochi/models/race_ethnicity.py @@ -0,0 +1,15 @@ +from django.db import models + +from .common import CommonInfo + + +class RaceEthnicity(CommonInfo): + race_ethnicity = models.CharField(max_length=255,) + is_visible = models.BooleanField(default=False,) + + class Meta: + verbose_name = "Race/Ethnicity" + verbose_name_plural = "Race/Ethnicities" + + def __str__(self): + return self.race_ethnicity diff --git a/coderdojochi/models/session.py b/coderdojochi/models/session.py new file mode 100644 index 00000000..d2a599f2 --- /dev/null +++ b/coderdojochi/models/session.py @@ -0,0 +1,232 @@ +from datetime import timedelta + +from django.core.validators import MaxValueValidator, MinValueValidator +from django.db import models +from django.urls.base import reverse +from django.utils import formats +from django.utils.functional import cached_property + +from .common import CommonInfo + + +class Session(CommonInfo): + from .course import Course + from .location import Location + from .mentor import Mentor + from .student import Student + + MALE = "male" + FEMALE = "female" + + GENDER_LIMITATION_CHOICES = ( + (MALE, "Male"), + (FEMALE, "Female"), + ) + + course = models.ForeignKey(Course, on_delete=models.CASCADE, limit_choices_to={"is_active": True},) + start_date = models.DateTimeField() + location = models.ForeignKey(Location, on_delete=models.CASCADE, limit_choices_to={"is_active": True},) + capacity = models.IntegerField(default=20,) + mentor_capacity = models.IntegerField(blank=True, null=True,) + instructor = models.ForeignKey( + Mentor, + on_delete=models.CASCADE, + related_name="session_instructor", + limit_choices_to={"user__groups__name": "Instructor"}, + ) + + # Pricing + cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) + minimum_cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) + maximum_cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) + + # Extra + additional_info = models.TextField(blank=True, null=True, help_text="Basic HTML allowed") + waitlist_mentors = models.ManyToManyField(Mentor, blank=True, related_name="session_waitlist_mentors",) + waitlist_students = models.ManyToManyField(Student, blank=True, related_name="session_waitlist_students",) + external_enrollment_url = models.CharField( + max_length=255, blank=True, null=True, help_text="When provided, local enrollment is disabled.", + ) + + is_active = models.BooleanField(default=False, help_text="Session is active.",) + is_public = models.BooleanField(default=False, help_text="Session is a public session.",) + password = models.CharField(blank=True, max_length=255,) + partner_message = models.TextField(blank=True,) + announced_date_mentors = models.DateTimeField(blank=True, null=True,) + announced_date_guardians = models.DateTimeField(blank=True, null=True,) + image_url = models.CharField(max_length=255, blank=True, null=True,) + bg_image = models.ImageField(blank=True, null=True,) + mentors_week_reminder_sent = models.BooleanField(default=False,) + mentors_day_reminder_sent = models.BooleanField(default=False,) + gender_limitation = models.CharField( + help_text="Limits the class to be only one gender.", + max_length=255, + choices=GENDER_LIMITATION_CHOICES, + blank=True, + null=True, + ) + override_minimum_age_limitation = models.IntegerField( + "Min Age", + help_text="Only update this if different from the default.", + blank=True, + null=True, + validators=[MinValueValidator(0), MaxValueValidator(100)], + ) + override_maximum_age_limitation = models.IntegerField( + "Max Age", + help_text="Only update this if different from the default.", + blank=True, + null=True, + validators=[MinValueValidator(0), MaxValueValidator(100)], + ) + online_video_link = models.URLField( + "Online Video Link", help_text="Zoom link with password.", blank=True, null=True, + ) + online_video_meeting_id = models.CharField( + "Online Video Meeting ID", help_text="XXX XXXX XXXX", max_length=255, blank=True, null=True, + ) + online_video_meeting_password = models.CharField( + "Online Video Meeting Password", + help_text="Plain text password shared by Zoom", + max_length=255, + blank=True, + null=True, + ) + online_video_description = models.TextField( + "Online Video Description", + help_text="Information on how to connect to the video call. Basic HTML allowed.", + blank=True, + null=True, + ) + + # kept for older records + old_end_date = models.DateTimeField(blank=True, null=True,) + old_mentor_start_date = models.DateTimeField(blank=True, null=True,) + old_mentor_end_date = models.DateTimeField(blank=True, null=True,) + + @property + def end_date(self): + # Some records have a defined record with the end date, + # rather than use the course's duration. + # We're keeping this for old records. + if self.old_end_date: + return self.old_end_date + + return self.start_date + self.course.duration + + @property + def mentor_start_date(self): + # Some records have a defined record with the mentor start date, + # rather than do the math. + # We're keeping this for old records. + if self.old_mentor_start_date: + return self.old_mentor_start_date + + return self.start_date - timedelta(hours=1) + + @property + def mentor_end_date(self): + # Some records have a defined record with the mentor start date, + # rather than do the math. + # We're keeping this for old records. + if self.old_mentor_end_date: + return self.old_mentor_end_date + + return self.end_date + timedelta(hours=1) + + @property + def minimum_age(self): + if self.override_minimum_age_limitation is not None: + return self.override_minimum_age_limitation + + return self.course.minimum_age + + @property + def maximum_age(self): + if self.override_maximum_age_limitation is not None: + return self.override_maximum_age_limitation + + return self.course.maximum_age + + def __str__(self): + date = formats.date_format(self.start_date, "SHORT_DATETIME_FORMAT") + return f"{self.course.title} | {date}" + + def save(self, *args, **kwargs): + if self.mentor_capacity is None: + self.mentor_capacity = int(self.capacity / 2) + + super(Session, self).save(*args, **kwargs) + + def get_absolute_url(self) -> str: + return reverse("session-detail", args=[str(self.id)]) + + def get_sign_up_url(self): + return reverse("session-sign-up", args=[str(self.id)]) + + def get_calendar_url(self): + return reverse("session-calendar", args=[str(self.id)]) + + def is_guardian_announced(self): + return self.announced_date_guardians is not None + + is_guardian_announced.boolean = True + is_guardian_announced.short_description = "Is Announced" + is_guardian_announced.admin_order_field = "announced_date_guardians" + + def get_mentor_orders(self): + from .mentor_order import MentorOrder + + return MentorOrder.objects.filter(session=self, is_active=True,).order_by("mentor__user__last_name") + + def get_checked_in_mentor_orders(self): + from .mentor_order import MentorOrder + + return MentorOrder.objects.filter(session=self, is_active=True, check_in__isnull=False).order_by( + "mentor__user__last_name" + ) + + def get_current_orders(self, checked_in=None): + from .order import Order + + if checked_in is not None: + if checked_in: + orders = ( + Order.objects.filter(is_active=True, session=self) + .exclude(check_in=None) + .order_by("student__last_name") + ) + else: + orders = Order.objects.filter(is_active=True, session=self, check_in=None).order_by( + "student__last_name" + ) + else: + orders = Order.objects.filter(is_active=True, session=self).order_by("check_in", "student__last_name") + + return orders + + def get_active_student_count(self): + from .order import Order + + return Order.objects.filter(is_active=True, session=self).values("student").count() + + def get_checked_in_students(self): + from .order import Order + + return Order.objects.filter(is_active=True, session=self).exclude(check_in=None).values("student") + + def get_mentor_capacity(self): + if self.mentor_capacity: + return self.mentor_capacity + else: + return int(self.capacity / 2) + + +class PartnerPasswordAccess(CommonInfo): + from .user import CDCUser + + user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) + session = models.ForeignKey(Session, on_delete=models.CASCADE,) + + class Meta: + db_table = "partner_password_access" diff --git a/coderdojochi/models/student.py b/coderdojochi/models/student.py new file mode 100644 index 00000000..3f3431de --- /dev/null +++ b/coderdojochi/models/student.py @@ -0,0 +1,87 @@ +from django.db import models +from django.utils import timezone + +from .common import CommonInfo +from .race_ethnicity import RaceEthnicity + + +class Student(CommonInfo): + from .guardian import Guardian + + guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE,) + first_name = models.CharField(max_length=255,) + last_name = models.CharField(max_length=255,) + birthday = models.DateTimeField() + gender = models.CharField(max_length=255,) + race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=True,) + school_name = models.CharField(max_length=255, null=True,) + school_type = models.CharField(max_length=255, null=True,) + medical_conditions = models.TextField(blank=True, null=True,) + medications = models.TextField(blank=True, null=True,) + photo_release = models.BooleanField( + "Photo Consent", + help_text=( + "I hereby give permission to We All Code to use " + "the student's image and/or likeness in promotional materials." + ), + default=False, + ) + consent = models.BooleanField( + "General Consent", + help_text=("I hereby give consent for the student signed up " "above to participate in We All Code."), + default=False, + ) + is_active = models.BooleanField(default=True,) + + def __str__(self): + return f"{self.first_name} {self.last_name}" + + def is_registered_for_session(self, session): + from .order import Order + + try: + Order.objects.get( + is_active=True, student=self, session=session, + ) + is_registered = True + except Exception: + is_registered = False + + return is_registered + + def get_age(self, date=timezone.now()): + return date.year - self.birthday.year - ((date.month, date.day) < (self.birthday.month, self.birthday.day)) + + get_age.short_description = "Age" + + def get_clean_gender(self): + MALE = ["male", "m", "boy", "nino", "masculino"] + FEMALE = ["female", "f", "girl", "femail", "femal", "femenino"] + + if self.gender.lower() in MALE: + return "male" + elif self.gender.lower() in FEMALE: + return "female" + else: + return "other" + + get_clean_gender.short_description = "Clean Gender" + + # returns True if the student age is between minimum_age and maximum_age + def is_within_age_range(self, minimum_age, maximum_age, date=timezone.now()): + age = self.get_age(date) + + if age >= minimum_age and age <= maximum_age: + return True + else: + return False + + def is_within_gender_limitation(self, limitation): + if limitation: + if self.get_clean_gender() in [limitation.lower(), "other"]: + return True + else: + return False + else: + return True + diff --git a/coderdojochi/models/user.py b/coderdojochi/models/user.py new file mode 100644 index 00000000..b7eaa297 --- /dev/null +++ b/coderdojochi/models/user.py @@ -0,0 +1,32 @@ +from django.contrib.auth.models import AbstractUser +from django.db import models +from django.urls import reverse +from django.utils import timezone +from django.utils.functional import cached_property + + +class CDCUser(AbstractUser): + + MENTOR = "mentor" + GUARDIAN = "guardian" + + ROLE_CHOICES = [ + (MENTOR, "mentor"), + (GUARDIAN, "guardian"), + ] + + role = models.CharField(choices=ROLE_CHOICES, max_length=10, blank=True, null=True,) + + admin_notes = models.TextField(blank=True, null=True,) + + @cached_property + def name(self): + return f"{self.first_name} {self.last_name}" + + def save(self, *args, **kwargs): + if self.pk is None: + self.last_login = timezone.now() + super(CDCUser, self).save(*args, **kwargs) + + def get_absolute_url(self): + return reverse("account_home") diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 9f02c564..b1724b2d 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -279,7 +279,7 @@ def session_stats(request, pk, template_name="session-stats.html"): if students_checked_in: attendance_percentage = round( - (float(current_orders_checked_in.count()) / float(session_obj.get_current_students().count())) * 100 + (float(current_orders_checked_in.count()) / float(session_obj.get_active_student_count())) * 100 ) else: diff --git a/coderdojochi/templates/guardian/session-detail.html b/coderdojochi/templates/guardian/session-detail.html index bc1075c3..8844e0f3 100644 --- a/coderdojochi/templates/guardian/session-detail.html +++ b/coderdojochi/templates/guardian/session-detail.html @@ -2,16 +2,15 @@ {% load static i18n humanize coderdojochi_extras %} -{% block title %}{% if session.course.code %}{{ session.course.code }}: {% endif %}{{ session.course.title }} on {{ session.start_date|date }} | {{ block.super }}{% endblock %} -{% block meta_facebook_title %}{% if session.course.code %}{{ session.course.code }}: {% endif %}{{ session.course.title }} on {{ session.start_date|date }} | {{ block.super }}{% endblock %} -{% block meta_twitter_title %}{% if session.course.code %}{{ session.course.code }}: {% endif %}{{ session.course.title }} on {{ session.start_date|date }} | {{ block.super }}{% endblock %} +{% block title %}{% if object.course.code %}{{ object.course.code }}: {% endif %}{{ object.course.title }} on {{ object.start_date|date }} | {{ block.super }}{% endblock %} +{% block meta_facebook_title %}{% if object.course.code %}{{ object.course.code }}: {% endif %}{{ object.course.title }} on {{ object.start_date|date }} | {{ block.super }}{% endblock %} +{% block meta_twitter_title %}{% if object.course.code %}{{ object.course.code }}: {% endif %}{{ object.course.title }} on {{ object.start_date|date }} | {{ block.super }}{% endblock %} + +{% block meta_description %}{{ object.course.description|striptags|safe }}{% endblock %} +{% block meta_facebook_description %}{{ object.course.description|striptags|safe }}{% endblock %} +{% block meta_twitter_description %}{{ object.course.description|striptags|safe }}{% endblock %} -{% block meta_description %}{{ session.course.description|striptags|safe }}{% endblock %} -{% block meta_facebook_description %}{{ session.course.description|striptags|safe }}{% endblock %} -{% block meta_twitter_description %}{{ session.course.description|striptags|safe }}{% endblock %} -{% comment %} {% block meta_facebook_image %}{% endblock %} {% endcomment %} -{% comment %} {% block meta_twitter_image %}{% endblock %} {% endcomment %} {% block body_class %}page-class-detail{% endblock %} @@ -21,178 +20,178 @@

Class Details & Enrollment

- {% if session.external_enrollment_url %} + {% if object.external_enrollment_url %} -

Enroll now

+

Enroll now

- {% else %} + {% else %} - {% if spots_remaining < 1 %} + {% if spots_remaining < 1 %} -

There are currently no available spots for this class. Please enroll in an upcoming class.

+

There are currently no available spots for this class. Please enroll in an upcoming class.

- {% else %} + {% else %} - {% if students %} + {% if students %} - {% if spots_remaining > 0 %} + {% if spots_remaining > 0 %} -

Enroll Student{{ students|pluralize }}

+

Enroll Student{{ students|pluralize }}

- {% else %} + {% else %} -

There are currently no available spots for this class. Please join the waitlist below and/or find another upcoming class.

+

There are currently no available spots for this class. Please join the waitlist below and/or find another upcoming class.

+ {% endif %} + + + + {% for student in students %} + + + + + {% endfor %} + +
{{ student.first_name }} {{ student.last_name|slice:":1" }} + {% student_session_order_count student=student session=session as student_is_enrolled %} + {% if spots_remaining > 0 or student_is_enrolled %} + {% student_register_link student session %} + {% else %} +
+ {% csrf_token %} + + + {% if student in object.waitlist_students.all %} + + + {% else %} + + + {% endif %} +
{% endif %} +
- - - {% for student in students %} - - - - - {% endfor %} - -
{{ student.first_name }} {{ student.last_name|slice:":1" }} - {% student_session_order_count student=student session=session as student_is_enrolled %} - {% if spots_remaining > 0 or student_is_enrolled %} - {% student_register_link student session %} - {% else %} -
- {% csrf_token %} - - - {% if student in session.waitlist_students.all %} - - - {% else %} - - - {% endif %} -
- {% endif %} -
- - Add another student - - {% else %} - -

Enroll

- - {% endif %} - - {% endif %} + Add another student - {% endif %} + {% else %} + +

Enroll

+ + {% endif %} + + {% endif %} + + {% endif %}
-

{% if session.course.code %}{{ session.course.code }}: {% endif%}{{ session.course.title }}

-

{{ session.course.description|safe }}

+

{% if object.course.code %}{{ object.course.code }}: {% endif%}{{ object.course.title }}

+

{{ object.course.description|safe }}

- {% if session.additional_info %} -

Additional Info:
{{ session.additional_info|safe }}

+ {% if object.additional_info %} +

Additional Info:
{{ object.additional_info|safe }}

{% endif %} - {% if session.online_video_link %} -

This will be a live online class via Zoom.

+ {% if object.online_video_link %} +

This will be a live online class via Zoom.

-

Technical Requirements

-

Computer: PC (Windows XP or newer), Mac (OSX 10.7 or newer), or Chromebook with at least a 2GHz processor and 2GB of RAM (4GB of RAM is recommended).

+

Technical Requirements

+

Computer: PC (Windows XP or newer), Mac (OSX 10.7 or newer), or Chromebook with at least a 2GHz processor and 2GB of RAM (4GB of RAM is recommended).

-

Internet: At least 1.2Mbps download and 600Kbps upload speeds. Go to fast.com to check your speed.

+

Internet: At least 1.2Mbps download and 600Kbps upload speeds. Go to fast.com to check your speed.

-

Webcam: Many laptops have an integrated webcam.

+

Webcam: Many laptops have an integrated webcam.

-

Microphone and Speakers: We highly recommend headphones with a built-in microphone, however any microphone and speakers will work in a quiet room.

+

Microphone and Speakers: We highly recommend headphones with a built-in microphone, however any microphone and speakers will work in a quiet room.

-

How To Join Online Class

-

About 10 minutes before class time, click the following link to join.

-

{{ session.online_video_link }}

+

How To Join Online Class

+

About 10 minutes before class time, click the following link to join.

+

{{ object.online_video_link }}

-

Meeting ID: {{ session.online_video_meeting_id }}
- Password: {{ session.online_video_meeting_password }}

+

Meeting ID: {{ object.online_video_meeting_id }}
+ Password: {{ object.online_video_meeting_password }}

{% endif %}
-
-
{{ session.start_date|date }}
-

- {{ session.start_date|time }} to {{ session.end_date|time }}
- Add to your calendar -

+
+
{{ object.start_date|date }}
+

{{ object.start_date|time }} to {{ object.end_date|time }}

+

Add to your calendar

+
{{ object.location.name }}
-
{{ session.location.name }}
- {% if session.location.address %} + {% if object.location.address %} -

{{ session.location.address }}, {{ session.location.city }}, {{ session.location.state }} {{ session.location.zip }}

+

{{ object.location.address }}, {{ object.location.city }}, {{ object.location.state }} {{ object.location.zip }}

{% else %} -

Join Zoom Meeting

+

Join Zoom Meeting

-

Meeting ID: {{ session.online_video_meeting_id }}
- Password: {{ session.online_video_meeting_password }}

+

+ Meeting ID: {{ object.online_video_meeting_id }}
+ Password: {{ object.online_video_meeting_password }} +

{% endif %}
Who
- {% if session.gender_limitation %} -

This class is limited to {{ session.gender_limitation }}s only.

+ {% if object.gender_limitation %} +

This class is limited to {{ object.gender_limitation }}s only.

{% endif %} - {% if session.gender_limitation %} -

{{ session.gender_limitation|title }}s between {{ session.minimum_age }} and {{ session.maximum_age }} of age. No computer skills required.

+ {% if object.gender_limitation %} +

{{ object.gender_limitation|title }}s between {{ object.minimum_age }} and {{ object.maximum_age }} of age. No computer skills required.

{% else %} -

Anyone between {{ session.minimum_age }} and {{ session.maximum_age }} of age. No computer skills required.

+

Anyone between {{ object.minimum_age }} and {{ object.maximum_age }} of age. No computer skills required.

{% endif %}
Cost
- {% if session.minimum_cost or session.maximm_cost %} -

${{ session.minimum_cost | floatformat:-2 }} – ${{ session.maximim_cost | floatformat:-2 }}

- {% elif session.cost > 0 %} -

${{ session.cost | floatformat:-2 }}

+ {% if object.minimum_cost or object.maximm_cost %} +

${{ object.minimum_cost | floatformat:-2 }} – ${{ object.maximim_cost | floatformat:-2 }}

+ {% elif object.cost > 0 %} +

${{ object.cost | floatformat:-2 }}

{% else %} -

Free to attend!

+

Free to attend!

{% endif %}
-{% if session.instructor %} +{% if object.instructor %}
-

About the instructor

-
-
- Photo of session.instructor }} -
-
-

{{ session.instructor }}

-

{{ session.instructor.bio|truncatechars_html:120 }}

-

Learn more about {{ session.instructor.user.first_name }}.

-
+

About the instructor

+
+
+ Photo of object.instructor }}
+
+

{{ object.instructor }}

+

{{ object.instructor.bio|truncatechars_html:120 }}

+

Learn more about {{ object.instructor.user.first_name }}.

+
+
{% endif %} {% if active_mentors %}
-

Meet the mentors

-
- {% for mentor in active_mentors %} - {% if mentor.is_public == True %} -
- {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.avatar.thumbnail %} -
- {% endif %} - {% endfor %} +

Meet the mentors

+
+ {% for mentor in active_mentors %} + {% if mentor.is_public == True %} +
+ {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.avatar.thumbnail %}
+ {% endif %} + {% endfor %} +
{% endif %} diff --git a/coderdojochi/templates/meeting-detail.html b/coderdojochi/templates/meeting-detail.html index 4165c2a2..91bfae5b 100644 --- a/coderdojochi/templates/meeting-detail.html +++ b/coderdojochi/templates/meeting-detail.html @@ -19,83 +19,82 @@

{{ object.meeting_type.title }} {{ object.start_date|date }}

-
-

{{ object.meeting_type.description|safe }}

- -

Meeting Information

- -

Who

-

Teachers, coders, writers, crafters, nurturers, leaders, tinkers, go getters to name a few. If you are passionate about coding and/or teaching children, join us. Everyone is welcome, regardless of technical ability.

- -

When

-

{{ object.start_date|date }} from {{ object.start_date|time }} to {{ object.end_date|time }}

-

Add to your calendar

- -

Where

-

{{ object.location.name }} {{ object.location.address }}
{{ object.location.city }}, {{ object.location.state }} {{ object.location.zip }}

- - {% if object.additional_info %} -

Additional Info

-

{{ object.additional_info|safe }}

- {% endif %} - - {% if object.get_current_mentors.count %} -

Attending

- -
- {% for mentor in object.get_current_mentors %} - {% if mentor.is_public == True %} - - {% endif %} - {% endfor %} -
- {% endif %} +
+

{{ object.meeting_type.description|safe }}

+ +

Meeting Information

+ +

Who

+

Teachers, coders, writers, crafters, nurturers, leaders, tinkers, go getters to name a few. If you are passionate about coding and/or teaching children, join us. Everyone is welcome, regardless of technical ability.

+ +

When

+

{{ object.start_date|date }} from {{ object.start_date|time }} to {{ object.end_date|time }}

+

Add to your calendar

+ +

Where

+

{{ object.location.name }} {{ object.location.address }}
{{ object.location.city }}, {{ object.location.state }} {{ object.location.zip }}

+ + {% if object.additional_info %} +

Additional Info

+

{{ object.additional_info|safe }}

+ {% endif %} + + {% if object.get_current_mentors.count %} +

Attending

+ +
+ {% for mentor in object.get_current_mentors %} + {% if mentor.is_public == True %} + + {% endif %} + {% endfor %}
- -
- {% if user.is_staff %} -
    - {% if object.is_active %} - -
  • Attending: {{ active_meeting_orders.count }}
  • - - {% if object.announced_date %} -
  • Announced: {{ object.announced_date|date }}
  • - {% else %} -
  • Announce
  • - {% endif %} - -
  • Check-in Meeting
  • - {% endif %} - - - {#
  • Meeting Stats
  • #} - -
  • Edit Meeting
  • -
- {% endif %} - - + {% endif %} +
+ +
+ {% if user.is_staff %} +
    + {% if object.is_active %} +
  • Attending: {{ active_meeting_orders.count }}
  • + + {% if object.announced_date %} +
  • Announced: {{ object.announced_date|date }}
  • + {% else %} +
  • Announce
  • + {% endif %} + +
  • Check-in Meeting
  • + {% endif %} + + + {#
  • Meeting Stats
  • #} + +
  • Edit Meeting
  • +
+ {% endif %} + + +
{% endblock %} diff --git a/coderdojochi/templates/public/session-detail.html b/coderdojochi/templates/public/session-detail.html new file mode 100644 index 00000000..124e18bb --- /dev/null +++ b/coderdojochi/templates/public/session-detail.html @@ -0,0 +1,125 @@ +{% extends "coderdojochi/_base.html" %} + +{% load static i18n humanize coderdojochi_extras %} + +{% block title %}{% if object.course.code %}{{ object.course.code }}: {% endif %}{{ object.course.title }} on {{ object.start_date|date }} | {{ block.super }}{% endblock %} +{% block meta_facebook_title %}{% if object.course.code %}{{ object.course.code }}: {% endif %}{{ object.course.title }} on {{ object.start_date|date }} | {{ block.super }}{% endblock %} +{% block meta_twitter_title %}{% if object.course.code %}{{ object.course.code }}: {% endif %}{{ object.course.title }} on {{ object.start_date|date }} | {{ block.super }}{% endblock %} + +{% block meta_description %}{{ object.course.description|striptags|safe }}{% endblock %} +{% block meta_facebook_description %}{{ object.course.description|striptags|safe }}{% endblock %} +{% block meta_twitter_description %}{{ object.course.description|striptags|safe }}{% endblock %} + +{% comment %} {% block meta_facebook_image %}{% endblock %} {% endcomment %} +{% comment %} {% block meta_twitter_image %}{% endblock %} {% endcomment %} + +{% block body_class %}page-class-detail{% endblock %} + +{% block contained_content %} + +

Class Details & Enrollment

+ +
+ + {% if object.external_enrollment_url %} +

Enroll now

+ {% else %} + {% if spots_remaining < 1 %} +

There are currently no available spots for this class.

+ {% endif %} +

Enroll now

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

{% if object.course.code %}{{ object.course.code }}: {% endif%}{{ object.course.title }}

+

{{ object.course.description|safe }}

+ + {% if object.additional_info %} +

Additional Info:
{{ object.additional_info|safe }}

+ {% endif %} + + {% if object.online_video_link %} +

This will be a live online class via Zoom.

+ +

Technical Requirements

+

Computer: PC (Windows XP or newer), Mac (OSX 10.7 or newer), or Chromebook with at least a 2GHz processor and 2GB of RAM (4GB of RAM is recommended).

+ +

Internet: At least 1.2Mbps download and 600Kbps upload speeds. Go to fast.com to check your speed.

+ +

Webcam: Many laptops have an integrated webcam.

+ +

Microphone and Speakers: We highly recommend headphones with a built-in microphone, however any microphone and speakers will work in a quiet room.

+ {% endif %} +
+ +
+
{{ object.start_date|date }}
+

{{ object.start_date|time:"f" }} to {{ object.end_date|time }}

+

Add to your calendar

+ + +
{{ object.location.name }}
+ {% if object.location.address %} +

{{ object.location.address }}, {{ object.location.city }}, {{ object.location.state }} {{ object.location.zip }}

+ {% else %} +

Details will be shared once you sign up.

+ {% endif %} + + +
Who
+ {% if object.gender_limitation %} +

This class is limited to {{ object.gender_limitation }}s only.

+

{{ object.gender_limitation|title }}s between {{ object.minimum_age }} and {{ object.maximum_age }} of age. No computer skills required.

+ {% else %} +

Anyone between {{ object.minimum_age }} and {{ object.maximum_age }} of age. No computer skills required.

+ {% endif %} + + +
Cost
+ {% if object.minimum_cost or object.maximm_cost %} +

${{ object.minimum_cost | floatformat:-2 }} – ${{ object.maximim_cost | floatformat:-2 }}

+ {% elif object.cost > 0 %} +

${{ object.cost | floatformat:-2 }}

+ {% else %} +

Free to attend!

+ {% endif %} + +
+
+ +{% if object.instructor %} +
+

About the instructor

+
+
+ Photo of object.instructor }} +
+
+

{{ object.instructor }}

+

{{ object.instructor.bio|truncatechars_html:120 }}

+

Learn more about {{ object.instructor.user.first_name }}.

+
+
+
+{% endif %} + +{% if active_mentors %} +
+

Meet the mentors

+
+ {% for mentor in active_mentors %} + {% if mentor.is_public == True %} +
+ {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.avatar.thumbnail %} +
+ {% endif %} + {% endfor %} +
+
+{% endif %} + +{% endblock %} diff --git a/coderdojochi/templates/session-detail.html b/coderdojochi/templates/session-detail.html deleted file mode 100644 index d443d327..00000000 --- a/coderdojochi/templates/session-detail.html +++ /dev/null @@ -1,127 +0,0 @@ -{% extends "coderdojochi/_base.html" %} - -{% load static i18n humanize coderdojochi_extras %} - -{% block title %}{% if session.course.code %}{{ session.course.code }}: {% endif %}{{ session.course.title }} on {{ session.start_date|date }} | {{ block.super }}{% endblock %} -{% block meta_facebook_title %}{% if session.course.code %}{{ session.course.code }}: {% endif %}{{ session.course.title }} on {{ session.start_date|date }} | {{ block.super }}{% endblock %} -{% block meta_twitter_title %}{% if session.course.code %}{{ session.course.code }}: {% endif %}{{ session.course.title }} on {{ session.start_date|date }} | {{ block.super }}{% endblock %} - -{% block meta_description %}{{ session.course.description|striptags|safe }}{% endblock %} -{% block meta_facebook_description %}{{ session.course.description|striptags|safe }}{% endblock %} -{% block meta_twitter_description %}{{ session.course.description|striptags|safe }}{% endblock %} - -{% comment %} {% block meta_facebook_image %}{% endblock %} {% endcomment %} -{% comment %} {% block meta_twitter_image %}{% endblock %} {% endcomment %} - -{% block body_class %}page-class-detail{% endblock %} - -{% block contained_content %} - -

Class Details & Enrollment

- -
- - {% if session.external_enrollment_url %} -

Enroll now

- {% else %} - {% if spots_remaining < 1 %} -

There are currently no available spots for this class.

- {% endif %} -

Enroll now

- {% endif %} - -
- -
- -
-

{% if session.course.code %}{{ session.course.code }}: {% endif%}{{ session.course.title }}

-

{{ session.course.description|safe }}

- - {% if session.additional_info %} -

Additional Info:
{{ session.additional_info|safe }}

- {% endif %} - - {% if session.online_video_link %} -

This will be a live online class via Zoom.

- -

Technical Requirements

-

Computer: PC (Windows XP or newer), Mac (OSX 10.7 or newer), or Chromebook with at least a 2GHz processor and 2GB of RAM (4GB of RAM is recommended).

- -

Internet: At least 1.2Mbps download and 600Kbps upload speeds. Go to fast.com to check your speed.

- -

Webcam: Many laptops have an integrated webcam.

- -

Microphone and Speakers: We highly recommend headphones with a built-in microphone, however any microphone and speakers will work in a quiet room.

- {% endif %} -
- -
-
{{ session.start_date|date }}
-

- {{ session.start_date|time }} to {{ session.end_date|time }}
- Add to your calendar -

- - -
{{ session.location.name }}
- {% if session.location.address %} -

{{ session.location.address }}, {{ session.location.city }}, {{ session.location.state }} {{ session.location.zip }}

- {% else %} -

Details will be shared once you sign up.

- {% endif %} - - -
Who
- {% if session.gender_limitation %} -

This class is limited to {{ session.gender_limitation }}s only.

-

{{ session.gender_limitation|title }}s between {{ session.minimum_age }} and {{ session.maximum_age }} of age. No computer skills required.

- {% else %} -

Anyone between {{ session.minimum_age }} and {{ session.maximum_age }} of age. No computer skills required.

- {% endif %} - - -
Cost
- {% if session.minimum_cost or session.maximm_cost %} -

${{ session.minimum_cost | floatformat:-2 }} – ${{ session.maximim_cost | floatformat:-2 }}

- {% elif session.cost > 0 %} -

${{ session.cost | floatformat:-2 }}

- {% else %} -

Free to attend!

- {% endif %} - -
-
- -{% if session.instructor %} -
-

About the instructor

-
-
- Photo of session.instructor }} -
-
-

{{ session.instructor }}

-

{{ session.instructor.bio|truncatechars_html:120 }}

-

Learn more about {{ session.instructor.user.first_name }}.

-
-
-
-{% endif %} - -{% if active_mentors %} -
-

Meet the mentors

-
- {% for mentor in active_mentors %} - {% if mentor.is_public == True %} -
- {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.avatar.thumbnail %} -
- {% endif %} - {% endfor %} -
-
-{% endif %} - -{% endblock %} diff --git a/coderdojochi/urls.py b/coderdojochi/urls.py index 84f4d195..7d22deb3 100644 --- a/coderdojochi/urls.py +++ b/coderdojochi/urls.py @@ -8,9 +8,18 @@ from django.views.generic import RedirectView from . import old_views -from .views.meetings import MeetingCalendarView, MeetingDetailView, MeetingsView, meeting_announce, meeting_sign_up -from .views.sessions import PasswordSessionView, SessionCalendarView, SessionDetailView, SessionSignUpView -from .views.welcome import WelcomeView +from .views import ( # SessionDetailView, + MeetingCalendarView, + MeetingDetailView, + MeetingsView, + PasswordSessionView, + SessionCalendarView, + SessionDetailView, + SessionSignUpView, + WelcomeView, + meeting_announce, + meeting_sign_up, +) admin.autodiscover() diff --git a/coderdojochi/views/__init__.py b/coderdojochi/views/__init__.py index e69de29b..d860a77e 100644 --- a/coderdojochi/views/__init__.py +++ b/coderdojochi/views/__init__.py @@ -0,0 +1,5 @@ +from .calendar import * +from .meetings import * +from .profile import * +from .sessions import * +from .welcome import * diff --git a/coderdojochi/views/guardian/__init__.py b/coderdojochi/views/guardian/__init__.py new file mode 100644 index 00000000..494eed44 --- /dev/null +++ b/coderdojochi/views/guardian/__init__.py @@ -0,0 +1 @@ +from .sessions import * diff --git a/coderdojochi/views/guardian/sessions.py b/coderdojochi/views/guardian/sessions.py new file mode 100644 index 00000000..3874c304 --- /dev/null +++ b/coderdojochi/views/guardian/sessions.py @@ -0,0 +1,22 @@ +from django.shortcuts import get_object_or_404 +from django.views.generic import DetailView + +from ...models import Guardian, Session + + +class SessionDetailView(DetailView): + model = Session + template_name = "guardian/session-detail.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + # print(kwargs["object"]) + + guardian = get_object_or_404(Guardian, user=self.request.user) + + context["students"] = guardian.get_students() + # context["spots_remaining"] = object.capacity - object.get_active_student_count() + + return context + diff --git a/coderdojochi/views/mentor/__init__.py b/coderdojochi/views/mentor/__init__.py new file mode 100644 index 00000000..494eed44 --- /dev/null +++ b/coderdojochi/views/mentor/__init__.py @@ -0,0 +1 @@ +from .sessions import * diff --git a/coderdojochi/views/mentor/sessions.py b/coderdojochi/views/mentor/sessions.py new file mode 100644 index 00000000..7fb6acec --- /dev/null +++ b/coderdojochi/views/mentor/sessions.py @@ -0,0 +1,17 @@ +from django.shortcuts import get_object_or_404 +from django.views.generic import DetailView + +from ...models import Mentor, Session + + +class SessionDetailView(DetailView): + model = Session + template_name = "mentor/session-detail.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + mentor = get_object_or_404(Mentor, user=self.request.user) + + return context + diff --git a/coderdojochi/views/public/__init__.py b/coderdojochi/views/public/__init__.py new file mode 100644 index 00000000..494eed44 --- /dev/null +++ b/coderdojochi/views/public/__init__.py @@ -0,0 +1 @@ +from .sessions import * diff --git a/coderdojochi/views/public/sessions.py b/coderdojochi/views/public/sessions.py new file mode 100644 index 00000000..db59d1cf --- /dev/null +++ b/coderdojochi/views/public/sessions.py @@ -0,0 +1,15 @@ +from django.shortcuts import get_object_or_404 +from django.views.generic import DetailView + +from ...models import Session + + +class SessionDetailView(DetailView): + model = Session + template_name = "public/session-detail.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + return context + diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index e2c2e672..e9655dc9 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -9,14 +9,17 @@ from django.utils import timezone from django.utils.decorators import method_decorator from django.utils.html import strip_tags -from django.views.generic import TemplateView +from django.views.generic import DetailView, TemplateView, View +from django.views.generic.base import RedirectView import arrow from coderdojochi.mixins import RoleRedirectMixin, RoleTemplateMixin -from coderdojochi.models import Guardian, Mentor, MentorOrder, Order, PartnerPasswordAccess, Session, Student +from coderdojochi.models import Guardian, Mentor, MentorOrder, Order, PartnerPasswordAccess, Session, Student, guardian from coderdojochi.util import email -from coderdojochi.views.calendar import CalendarView + +from . import guardian, mentor, public +from .calendar import CalendarView logger = logging.getLogger(__name__) @@ -98,107 +101,118 @@ def session_confirm_guardian(request, session_obj, order, student): ) -class SessionDetailView(RoleRedirectMixin, RoleTemplateMixin, TemplateView): - template_name = "session-detail.html" - - def dispatch(self, request, *args, **kwargs): - session_obj = get_object_or_404(Session, id=kwargs["pk"]) - - if request.method == "GET": - if session_obj.password and not self.validate_partner_session_access(self.request, kwargs["pk"]): - return redirect(reverse("session-password", kwargs=kwargs)) - - if request.user.is_authenticated and request.user.role: - if "enroll" in request.GET or "enroll" in kwargs: - return self.enroll_redirect(request, session_obj) - - kwargs["session_obj"] = session_obj - return super(SessionDetailView, self).dispatch(request, *args, **kwargs) - - def enroll_redirect(self, request, session_obj): - if request.user.role == "mentor": - return redirect("session-sign-up", pk=session_obj.id) - - guardian = get_object_or_404(Guardian, user=request.user) - student = get_object_or_404(Student, guardian=guardian, id=(int(request.GET["student"]))) - - if student: - return redirect("session-sign-up", pk=session_obj.id, student_id=student.id) - - return redirect(f"{reverse('welcome')}?next={session_obj.get_absolute_url()}&enroll=True") - - def validate_partner_session_access(self, request, pk): - authed_sessions = request.session.get("authed_partner_sessions") - - if authed_sessions and pk in authed_sessions: - if request.user.is_authenticated: - PartnerPasswordAccess.objects.get_or_create(session_id=pk, user=request.user) - return True - +class SessionDetailView(View): + def get(self, request, *args, **kwargs): if request.user.is_authenticated: - try: - PartnerPasswordAccess.objects.get(session_id=pk, user_id=request.user.id) - except PartnerPasswordAccess.DoesNotExist: - return False - else: - return True - - else: - return False - - def get_context_data(self, **kwargs): - context = super(SessionDetailView, self).get_context_data(**kwargs) - session_obj = kwargs["session_obj"] - context["session"] = session_obj - - upcoming_classes = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") - context["upcoming_classes"] = upcoming_classes - - active_mentors = Mentor.objects.filter( - id__in=MentorOrder.objects.filter(session=session_obj, is_active=True).values("mentor__id") - ) - context["active_mentors"] = active_mentors - - if self.request.user.is_authenticated: - if self.request.user.role == "mentor": - account = get_object_or_404(Mentor, user=self.request.user) - session_orders = MentorOrder.objects.filter(session=session_obj, is_active=True,) - context["mentor_signed_up"] = session_orders.filter(mentor=account).exists() - - context["spots_remaining"] = session_obj.get_mentor_capacity() - session_orders.count() + if request.user.role == "mentor": + return mentor.SessionDetailView.as_view()(request, *args, **kwargs) else: - account = get_object_or_404(Guardian, user=self.request.user) - context["students"] = account.get_students() - context["spots_remaining"] = session_obj.capacity - session_obj.get_current_students().count() - context["account"] = account - else: - context["upcoming_classes"] = upcoming_classes.filter(is_public=True) - context["spots_remaining"] = session_obj.capacity - session_obj.get_current_students().count() - - return context - - def post(self, request, *args, **kwargs): - session_obj = kwargs["session_obj"] - if "waitlist" not in request.POST: - messages.error(request, "Invalid request, please try again.") - return redirect(session_obj.get_absolute_url()) - - if request.POST["waitlist"] == "student": - account = Student.objects.get(id=request.POST["account_id"]) - waitlist_attr = "waitlist_students" - else: - account = Guardian.objects.get(id=request.POST["account_id"]) - waitlist_attr = "waitlist_guardians" - - if request.POST["remove"] == "true": - getattr(session_obj, waitlist_attr).remove(account) - session_obj.save() - messages.success(request, "You have been removed from the waitlist. Thanks for letting us know.") - else: - getattr(session_obj, waitlist_attr).add(account) - session_obj.save() - messages.success(request, "Added to waitlist successfully.") - return redirect(session_obj.get_absolute_url()) + return guardian.SessionDetailView.as_view()(request, *args, **kwargs) + return public.SessionDetailView.as_view()(request, *args, **kwargs) + + +# class SessionDetailView(RoleRedirectMixin, RoleTemplateMixin, TemplateView): +# template_name = "session-detail.html" + +# def dispatch(self, request, *args, **kwargs): +# session_obj = get_object_or_404(Session, id=kwargs["pk"]) + +# if request.method == "GET": +# if session_obj.password and not self.validate_partner_session_access(self.request, kwargs["pk"]): +# return redirect(reverse("session-password", kwargs=kwargs)) + +# if request.user.is_authenticated and request.user.role: +# if "enroll" in request.GET or "enroll" in kwargs: +# return self.enroll_redirect(request, session_obj) + +# # kwargs["session_obj"] = session_obj +# return super(SessionDetailView, self).dispatch(request, *args, **kwargs) + +# def enroll_redirect(self, request, session_obj): +# if request.user.role == "mentor": +# return redirect("session-sign-up", pk=session_obj.id) + +# guardian = get_object_or_404(Guardian, user=request.user) +# student = get_object_or_404(Student, guardian=guardian, id=(int(request.GET["student"]))) + +# if student: +# return redirect("session-sign-up", pk=session_obj.id, student_id=student.id) + +# return redirect(f"{reverse('welcome')}?next={session_obj.get_absolute_url()}&enroll=True") + +# def validate_partner_session_access(self, request, pk): +# authed_sessions = request.session.get("authed_partner_sessions") + +# if authed_sessions and pk in authed_sessions: +# if request.user.is_authenticated: +# PartnerPasswordAccess.objects.get_or_create(session_id=pk, user=request.user) +# return True + +# if request.user.is_authenticated: +# try: +# PartnerPasswordAccess.objects.get(session_id=pk, user_id=request.user.id) +# except PartnerPasswordAccess.DoesNotExist: +# return False +# else: +# return True + +# else: +# return False + +# def get_context_data(self, **kwargs): +# print(kwargs["session_obj"].__dict__) +# context = super(SessionDetailView, self).get_context_data(**kwargs) +# session_obj = kwargs["session_obj"] +# context["session"] = session_obj + +# upcoming_classes = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") +# context["upcoming_classes"] = upcoming_classes + +# active_mentors = Mentor.objects.filter( +# id__in=MentorOrder.objects.filter(session=session_obj, is_active=True).values("mentor__id") +# ) +# context["active_mentors"] = active_mentors + +# if self.request.user.is_authenticated: +# if self.request.user.role == "mentor": +# account = get_object_or_404(Mentor, user=self.request.user) +# session_orders = MentorOrder.objects.filter(session=session_obj, is_active=True,) +# context["mentor_signed_up"] = session_orders.filter(mentor=account).exists() + +# context["spots_remaining"] = session_obj.get_mentor_capacity() - session_orders.count() +# else: +# account = get_object_or_404(Guardian, user=self.request.user) +# context["students"] = account.get_students() +# context["spots_remaining"] = session_obj.capacity - session_obj.get_active_student_count() +# context["account"] = account +# else: +# context["upcoming_classes"] = upcoming_classes.filter(is_public=True) +# context["spots_remaining"] = session_obj.capacity - session_obj.objects.get_active_student_count() + +# return context + +# def post(self, request, *args, **kwargs): +# session_obj = kwargs["session_obj"] +# if "waitlist" not in request.POST: +# messages.error(request, "Invalid request, please try again.") +# return redirect(session_obj.get_absolute_url()) + +# if request.POST["waitlist"] == "student": +# account = Student.objects.get(id=request.POST["account_id"]) +# waitlist_attr = "waitlist_students" +# else: +# account = Guardian.objects.get(id=request.POST["account_id"]) +# waitlist_attr = "waitlist_guardians" + +# if request.POST["remove"] == "true": +# getattr(session_obj, waitlist_attr).remove(account) +# session_obj.save() +# messages.success(request, "You have been removed from the waitlist. Thanks for letting us know.") +# else: +# getattr(session_obj, waitlist_attr).add(account) +# session_obj.save() +# messages.success(request, "Added to waitlist successfully.") +# return redirect(session_obj.get_absolute_url()) class SessionSignUpView(RoleRedirectMixin, RoleTemplateMixin, TemplateView): @@ -264,7 +278,7 @@ def student_limitations(self, student, session_obj, user_signed_up): f"{session_obj.minimum_age} and {session_obj.maximum_age}." ) - if not user_signed_up and session_obj.capacity <= session_obj.get_current_students().count(): + if not user_signed_up and session_obj.capacity <= session_obj.get_active_student_count(): return "Sorry this class has sold out. Please sign up for the wait list and/or check back later." return False diff --git a/fixtures/17-weallcode.boardmember.json b/fixtures/17-weallcode.boardmember.json index 38e67fc6..ad061426 100644 --- a/fixtures/17-weallcode.boardmember.json +++ b/fixtures/17-weallcode.boardmember.json @@ -7,6 +7,7 @@ "role": "Chair", "description": "Founder & CEO, Red Squirrel Technologies", "linkedin": "https://www.linkedin.com/in/redsquirrel/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -21,6 +22,7 @@ "role": "Vice Chair", "description": "CEO & Principal Consultant, 4 Point Consulting", "linkedin": "https://www.linkedin.com/in/christynlyons/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -35,6 +37,7 @@ "role": "Secretary", "description": "Asst. Director of Program Development, GWTP", "linkedin": "https://www.linkedin.com/in/michael-cotter-mpp-3a3a805/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -49,6 +52,7 @@ "role": "Treasurer", "description": "Founder & Principal, Blackwood Group LLC", "linkedin": "https://www.linkedin.com/in/jose-duarte-94469612/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -63,6 +67,7 @@ "role": "Director", "description": "Founder & CEO, We All Code", "linkedin": "https://www.linkedin.com/in/karbassi/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T21:48:56.906Z", diff --git a/fixtures/18-weallcode.associateboardmember.json b/fixtures/18-weallcode.associateboardmember.json index ca12d719..5ec45817 100644 --- a/fixtures/18-weallcode.associateboardmember.json +++ b/fixtures/18-weallcode.associateboardmember.json @@ -7,6 +7,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/alexandra-rodriguez-beuerman-94990a59/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -21,6 +22,7 @@ "role": "Treasurer", "description": null, "linkedin": "https://www.linkedin.com/in/annabkronauer/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -35,6 +37,7 @@ "role": "Vice Chair", "description": null, "linkedin": "https://www.linkedin.com/in/brendan-mclaughlin-864b3413/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -49,6 +52,7 @@ "role": "Chair", "description": null, "linkedin": "https://www.linkedin.com/in/carolyn-potts-b416a41b/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -63,6 +67,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/clairelipskey/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -77,6 +82,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/danielmconrad/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -91,6 +97,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/jordanpolonsky/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -105,6 +112,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/joseph-fowler/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -119,6 +127,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/katherineeevans/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -133,6 +142,7 @@ "role": "Secretary", "description": null, "linkedin": "https://www.linkedin.com/in/ltramos7/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -147,6 +157,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/matthew-felz-53025940/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -161,6 +172,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/sconstantinides/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", @@ -175,6 +187,7 @@ "role": "Director", "description": null, "linkedin": "https://www.linkedin.com/in/tatyanashestopalova/", + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T20:40:00Z", diff --git a/fixtures/19-weallcode.staffmember.json b/fixtures/19-weallcode.staffmember.json index 738c6780..1e5524c7 100644 --- a/fixtures/19-weallcode.staffmember.json +++ b/fixtures/19-weallcode.staffmember.json @@ -6,6 +6,7 @@ "name": "Ali Karbassi", "description": null, "linkedin": null, + "join_date": "2020-01-01", "is_active": true, "created_at": "2020-05-18T20:40:00Z", "updated_at": "2020-05-18T21:48:23.037Z", diff --git a/weallcode/admin.py b/weallcode/admin.py index 587ad4f1..cc2a64ea 100644 --- a/weallcode/admin.py +++ b/weallcode/admin.py @@ -38,6 +38,7 @@ def member_image(self, obj): "fields": ( "name", "role", + "join_date", "description", "linkedin", "image", @@ -85,6 +86,7 @@ def member_image(self, obj): "fields": ( "name", "role", + "join_date", "description", "linkedin", "image", @@ -109,6 +111,7 @@ def member_image(self, obj): "member_image", "name", "role", + "join_date", "is_active", ] @@ -132,6 +135,7 @@ def member_image(self, obj): "fields": ( "name", "role", + "join_date", "description", "linkedin", "image", diff --git a/weallcode/migrations/0007_auto_20201229_1542.py b/weallcode/migrations/0007_auto_20201229_1542.py new file mode 100644 index 00000000..15e99992 --- /dev/null +++ b/weallcode/migrations/0007_auto_20201229_1542.py @@ -0,0 +1,44 @@ +# Generated by Django 3.1.2 on 2020-12-29 21:42 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('weallcode', '0006_auto_20200615_1451'), + ] + + operations = [ + migrations.AlterModelOptions( + name='associateboardmember', + options={}, + ), + migrations.AlterModelOptions( + name='boardmember', + options={}, + ), + migrations.AlterModelOptions( + name='staffmember', + options={}, + ), + migrations.AddField( + model_name='associateboardmember', + name='join_date', + field=models.DateField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Join Date'), + preserve_default=False, + ), + migrations.AddField( + model_name='boardmember', + name='join_date', + field=models.DateField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Join Date'), + preserve_default=False, + ), + migrations.AddField( + model_name='staffmember', + name='join_date', + field=models.DateField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Join Date'), + preserve_default=False, + ), + ] diff --git a/weallcode/models/__init__.py b/weallcode/models/__init__.py new file mode 100644 index 00000000..dca98aa4 --- /dev/null +++ b/weallcode/models/__init__.py @@ -0,0 +1,4 @@ +from .associate_board_member import * +from .board_member import * +from .common import * +from .staff import * diff --git a/weallcode/models/associate_board_member.py b/weallcode/models/associate_board_member.py new file mode 100644 index 00000000..b3daf6b4 --- /dev/null +++ b/weallcode/models/associate_board_member.py @@ -0,0 +1,16 @@ +from collections import defaultdict +from itertools import chain + +from django.db import models + +from .common import CommonBoardMemberManager, CommonInfo + + +class AssociateBoardMember(CommonInfo): + + image = models.ImageField(upload_to="associate-board/", blank=True, null=True,) + + objects = CommonBoardMemberManager() + + def __str__(self): + return self.name diff --git a/weallcode/models/board_member.py b/weallcode/models/board_member.py new file mode 100644 index 00000000..d722aa2f --- /dev/null +++ b/weallcode/models/board_member.py @@ -0,0 +1,14 @@ +from django.db import models + +from .common import CommonBoardMemberManager, CommonInfo + + +class BoardMember(CommonInfo): + + image = models.ImageField(upload_to="board/", blank=True, null=True,) + + objects = CommonBoardMemberManager() + + def __str__(self): + return self.name + diff --git a/weallcode/models.py b/weallcode/models/common.py similarity index 60% rename from weallcode/models.py rename to weallcode/models/common.py index 64ba59cf..6bf0d2e9 100644 --- a/weallcode/models.py +++ b/weallcode/models/common.py @@ -1,4 +1,8 @@ +from collections import defaultdict +from itertools import chain + from django.db import models +from django.db.models import Case, When CHAIR = "Chair" VICE_CHAIR = "Vice Chair" @@ -18,50 +22,29 @@ class CommonInfo(models.Model): - name = models.CharField(max_length=255,) - role = models.CharField(choices=ROLE_CHOICES, max_length=255, default=DIRECTOR,) - image = models.ImageField(upload_to="staff/", blank=True, null=True,) - description = models.CharField(max_length=255, blank=True, null=True,) - linkedin = models.URLField(max_length=200, blank=True, null=True,) - - # Active + join_date = models.DateField("Join Date", auto_now_add=True) is_active = models.BooleanField(default=True,) - - # Auto create/update created_at = models.DateTimeField(auto_now_add=True,) - updated_at = models.DateTimeField(auto_now=True,) class Meta: abstract = True -class StaffMember(CommonInfo): - - role = models.CharField(max_length=255,) - - image = models.ImageField(upload_to="staff/", blank=True, null=True,) - - def __str__(self): - return self.name - - -class BoardMember(CommonInfo): - - image = models.ImageField(upload_to="board/", blank=True, null=True,) - - def __str__(self): - return self.name - - -class AssociateBoardMember(CommonInfo): +class CommonBoardMemberManager(models.Manager): + def get_queryset(self): + return super().get_queryset().filter(is_active=True) - image = models.ImageField(upload_to="associate-board/", blank=True, null=True,) + def get_sorted(self): + """ + Reordering by role. + """ - def __str__(self): - return self.name + roles = [CHAIR, VICE_CHAIR, TREASURER, SECRETARY, DIRECTOR] + order = Case(*[When(role=role, then=pos) for pos, role in enumerate(roles)]) + return self.get_queryset().filter(role__in=roles).order_by(order, "join_date", "name") diff --git a/weallcode/models/staff.py b/weallcode/models/staff.py new file mode 100644 index 00000000..b02a9129 --- /dev/null +++ b/weallcode/models/staff.py @@ -0,0 +1,21 @@ +from django.db import models + +from .common import CommonInfo + + +class StaffMemberManager(models.Manager): + def get_queryset(self): + return super().get_queryset().filter(is_active=True) + + +class StaffMember(CommonInfo): + + role = models.CharField(max_length=255,) + + image = models.ImageField(upload_to="staff/", blank=True, null=True,) + + objects = StaffMemberManager() + + def __str__(self): + return self.name + diff --git a/weallcode/templates/weallcode/_base.html b/weallcode/templates/weallcode/_base.html index dfb162a1..0f1edae0 100644 --- a/weallcode/templates/weallcode/_base.html +++ b/weallcode/templates/weallcode/_base.html @@ -17,12 +17,6 @@ - - - - - - {% block extra_meta %}{% endblock %} diff --git a/weallcode/views.py b/weallcode/views.py index 49cda5bf..eabd6c83 100644 --- a/weallcode/views.py +++ b/weallcode/views.py @@ -1,274 +1,274 @@ -from collections import defaultdict -from datetime import datetime -from itertools import chain +# from collections import defaultdict +# from datetime import datetime +# from itertools import chain -from django.conf import settings -from django.contrib import messages, sitemaps -from django.contrib.auth import get_user_model -from django.db.models import Count -from django.urls import reverse, reverse_lazy -from django.utils import timezone -from django.views.generic import FormView, TemplateView +# from django.conf import settings +# from django.contrib import messages, sitemaps +# from django.contrib.auth import get_user_model +# from django.db.models import Count +# from django.urls import reverse, reverse_lazy +# from django.utils import timezone +# from django.views.generic import FormView, TemplateView -from meta.views import MetadataMixin -from sentry_sdk import capture_message +# from meta.views import MetadataMixin +# from sentry_sdk import capture_message -from coderdojochi.models import Course, Mentor, Session +# from coderdojochi.models import Course, Mentor, Session -from .forms import ContactForm -from .models import AssociateBoardMember, BoardMember, StaffMember +# from .forms import ContactForm +# from .models import AssociateBoardMember, BoardMember, StaffMember -User = get_user_model() +# User = get_user_model() -def page_not_found_view(*args, **kwargs): - print("page_not_found_view") - capture_message("Page not found!", level="error") +# def page_not_found_view(*args, **kwargs): +# print("page_not_found_view") +# capture_message("Page not found!", level="error") - return render( - request, "404.html", {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, status=404, - ) +# return render( +# request, "404.html", {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, status=404, +# ) -class DefaultMetaTags(MetadataMixin): - title = None - description = ( - "We All Code is volunteer run nonprofit organization that teaches web, game, and app development to " - "youth ages 7 to 17 free of charge." - ) - image = "weallcode/images/photos/real-coding-skills.jpg" - twitter_card = "summary_large_image" - twitter_creator = "@weallcode" - twitter_site = "@weallcode" +# class DefaultMetaTags(MetadataMixin): +# title = None +# description = ( +# "We All Code is volunteer run nonprofit organization that teaches web, game, and app development to " +# "youth ages 7 to 17 free of charge." +# ) +# image = "weallcode/images/photos/real-coding-skills.jpg" +# twitter_card = "summary_large_image" +# twitter_creator = "@weallcode" +# twitter_site = "@weallcode" -class HomeView(DefaultMetaTags, TemplateView): - template_name = "weallcode/home.html" - url = reverse_lazy("weallcode-home") +# class HomeView(DefaultMetaTags, TemplateView): +# template_name = "weallcode/home.html" +# url = reverse_lazy("weallcode-home") - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) - sessions = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") +# sessions = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") - if not self.request.user.is_authenticated or not self.request.user.role == "mentor": - sessions = sessions.filter(is_public=True) +# if not self.request.user.is_authenticated or not self.request.user.role == "mentor": +# sessions = sessions.filter(is_public=True) - if len(sessions) > 0: - context["next_session"] = sessions[0] +# if len(sessions) > 0: +# context["next_session"] = sessions[0] - return context +# return context -class OurStoryView(DefaultMetaTags, TemplateView): - template_name = "weallcode/our_story.html" - url = reverse_lazy("weallcode-our-story") +# class OurStoryView(DefaultMetaTags, TemplateView): +# template_name = "weallcode/our_story.html" +# url = reverse_lazy("weallcode-our-story") - title = f"Our Story | We All Code" +# title = f"Our Story | We All Code" -class ProgramsView(DefaultMetaTags, TemplateView): - template_name = "weallcode/programs.html" - url = reverse_lazy("weallcode-programs") +# class ProgramsView(DefaultMetaTags, TemplateView): +# template_name = "weallcode/programs.html" +# url = reverse_lazy("weallcode-programs") - title = f"Programs | We All Code" +# title = f"Programs | We All Code" - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) - # WEEKEND CLASSES - weekend_classes = Session.objects.filter( - is_active=True, start_date__gte=timezone.now(), course__course_type=Course.WEEKEND, - ).order_by("start_date") +# # WEEKEND CLASSES +# weekend_classes = Session.objects.filter( +# is_active=True, start_date__gte=timezone.now(), course__course_type=Course.WEEKEND, +# ).order_by("start_date") - if not self.request.user.is_authenticated or not self.request.user.role == "mentor": - weekend_classes = weekend_classes.filter(is_public=True) +# if not self.request.user.is_authenticated or not self.request.user.role == "mentor": +# weekend_classes = weekend_classes.filter(is_public=True) - context["weekend_classes"] = weekend_classes +# context["weekend_classes"] = weekend_classes - # SUMMER CAMP CLASSES - summer_camp_classes = Session.objects.filter( - is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, - ).order_by("start_date") +# # SUMMER CAMP CLASSES +# summer_camp_classes = Session.objects.filter( +# is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, +# ).order_by("start_date") - if not self.request.user.is_authenticated or not self.request.user.role == "mentor": - summer_camp_classes = summer_camp_classes.filter(is_public=True) +# if not self.request.user.is_authenticated or not self.request.user.role == "mentor": +# summer_camp_classes = summer_camp_classes.filter(is_public=True) - context["summer_camp_classes"] = summer_camp_classes +# context["summer_camp_classes"] = summer_camp_classes - return context +# return context -class ProgramsSummerCampsView(DefaultMetaTags, TemplateView): - template_name = "weallcode/programs-summer-camps.html" - url = reverse_lazy("weallcode-programs-summer-camps") +# class ProgramsSummerCampsView(DefaultMetaTags, TemplateView): +# template_name = "weallcode/programs-summer-camps.html" +# url = reverse_lazy("weallcode-programs-summer-camps") - title = f"Summer Camps | We All Code" +# title = f"Summer Camps | We All Code" - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) - # SUMMER CAMP CLASSES - summer_camp_classes = Session.objects.filter( - is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, - ).order_by("start_date") +# # SUMMER CAMP CLASSES +# summer_camp_classes = Session.objects.filter( +# is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, +# ).order_by("start_date") - if not self.request.user.is_authenticated or not self.request.user.role == "mentor": - summer_camp_classes = summer_camp_classes.filter(is_public=True) +# if not self.request.user.is_authenticated or not self.request.user.role == "mentor": +# summer_camp_classes = summer_camp_classes.filter(is_public=True) - context["summer_camp_classes"] = summer_camp_classes +# context["summer_camp_classes"] = summer_camp_classes - return context +# return context -class TeamView(DefaultMetaTags, TemplateView): - template_name = "weallcode/team.html" - url = reverse_lazy("weallcode-team") +# class TeamView(DefaultMetaTags, TemplateView): +# template_name = "weallcode/team.html" +# url = reverse_lazy("weallcode-team") - title = f"Team | We All Code" +# title = f"Team | We All Code" - # Staff - def get_staff(self, context): - context["staff"] = StaffMember.objects.filter(is_active=True,) +# # Staff +# def get_staff(self, context): +# context["staff"] = StaffMember.objects.filter(is_active=True,) - return context +# return context - # Board Members - def get_board(self, context): - board = BoardMember.objects.filter(is_active=True,) +# # Board Members +# def get_board(self, context): +# board = BoardMember.objects.filter(is_active=True,) - # NOTE: We're splitting and reordering them manually since the website requires a specific order. - roles = defaultdict(list) +# # NOTE: We're splitting and reordering them manually since the website requires a specific order. +# roles = defaultdict(list) - for person in board: - roles[person.role].append(person) +# for person in board: +# roles[person.role].append(person) - # The correct order - context["board"] = list( - chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) - ) +# # The correct order +# context["board"] = list( +# chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) +# ) - return context +# return context - # Associate Board Members - def get_associate_board(self, context): - associate_board = AssociateBoardMember.objects.filter(is_active=True,).order_by("name") +# # Associate Board Members +# def get_associate_board(self, context): +# associate_board = AssociateBoardMember.objects.filter(is_active=True,).order_by("name") - # NOTE: We're splitting and reordering them manually since the website requires a specific order. - roles = defaultdict(list) +# # NOTE: We're splitting and reordering them manually since the website requires a specific order. +# roles = defaultdict(list) - for person in associate_board: - roles[person.role].append(person) +# for person in associate_board: +# roles[person.role].append(person) - # The correct order - context["associate_board"] = list( - chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) - ) +# # The correct order +# context["associate_board"] = list( +# chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) +# ) - return context +# return context - # Instructors - def get_instructors(self, context, volunteers): - context["instructors"] = volunteers.filter(user__groups__name__in=["Instructor"],).order_by("user__first_name") +# # Instructors +# def get_instructors(self, context, volunteers): +# context["instructors"] = volunteers.filter(user__groups__name__in=["Instructor"],).order_by("user__first_name") - return context +# return context - # Volunteers - def get_volunteers(self, context, volunteers): - all_volunteers = volunteers.annotate(session_count=Count("mentororder")).order_by( - "-user__role", "-session_count" - ) +# # Volunteers +# def get_volunteers(self, context, volunteers): +# all_volunteers = volunteers.annotate(session_count=Count("mentororder")).order_by( +# "-user__role", "-session_count" +# ) - mentors = [] - volunteers = [] +# mentors = [] +# volunteers = [] - for volunteer in all_volunteers: - if volunteer.session_count >= 10: - mentors += [volunteer] - else: - volunteers += [volunteer] +# for volunteer in all_volunteers: +# if volunteer.session_count >= 10: +# mentors += [volunteer] +# else: +# volunteers += [volunteer] - context["top_mentors"] = mentors - # context['other_mentors'] = mentors[8:] - context["top_volunteers"] = volunteers[0:16] - context["other_volunteers"] = volunteers[16:] +# context["top_mentors"] = mentors +# # context['other_mentors'] = mentors[8:] +# context["top_volunteers"] = volunteers[0:16] +# context["other_volunteers"] = volunteers[16:] - return context +# return context - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) +# def get_context_data(self, **kwargs): +# context = super().get_context_data(**kwargs) - volunteers = Mentor.objects.select_related("user").filter( - is_active=True, is_public=True, background_check=True, avatar_approved=True, - ) +# volunteers = Mentor.objects.select_related("user").filter( +# is_active=True, is_public=True, background_check=True, avatar_approved=True, +# ) - context = self.get_staff(context) - context = self.get_board(context) - context = self.get_associate_board(context) - context = self.get_instructors(context, volunteers) - context = self.get_volunteers(context, volunteers) +# context = self.get_staff(context) +# context = self.get_board(context) +# context = self.get_associate_board(context) +# context = self.get_instructors(context, volunteers) +# context = self.get_volunteers(context, volunteers) - return context +# return context -class JoinUsView(DefaultMetaTags, FormView): - template_name = "weallcode/join_us.html" - form_class = ContactForm - url = reverse_lazy("weallcode-join-us") - success_url = reverse_lazy("weallcode-join-us") +# class JoinUsView(DefaultMetaTags, FormView): +# template_name = "weallcode/join_us.html" +# form_class = ContactForm +# url = reverse_lazy("weallcode-join-us") +# success_url = reverse_lazy("weallcode-join-us") - title = f"Join Us | We All Code" +# title = f"Join Us | We All Code" - def form_valid(self, form): - # This method is called when valid form data has been POSTed. - # It should return an HttpResponse. - form.send_email() - messages.success( - self.request, "Thank you for contacting us! We will respond as soon as possible.", - ) +# def form_valid(self, form): +# # This method is called when valid form data has been POSTed. +# # It should return an HttpResponse. +# form.send_email() +# messages.success( +# self.request, "Thank you for contacting us! We will respond as soon as possible.", +# ) - return super().form_valid(form) +# return super().form_valid(form) -class AssociateBoardView(DefaultMetaTags, TemplateView): - template_name = "weallcode/associate_board.html" - url = reverse_lazy("weallcode-associate-board") +# class AssociateBoardView(DefaultMetaTags, TemplateView): +# template_name = "weallcode/associate_board.html" +# url = reverse_lazy("weallcode-associate-board") - title = f"Join our Associate Board | We All Code" +# title = f"Join our Associate Board | We All Code" -class PrivacyView(DefaultMetaTags, TemplateView): - template_name = "weallcode/privacy.html" - url = reverse_lazy("weallcode-privacy") +# class PrivacyView(DefaultMetaTags, TemplateView): +# template_name = "weallcode/privacy.html" +# url = reverse_lazy("weallcode-privacy") - title = f"Privacy & Terms | We All Code" +# title = f"Privacy & Terms | We All Code" -class CreditsView(DefaultMetaTags, TemplateView): - template_name = "weallcode/credits.html" - url = reverse_lazy("weallcode-credits") +# class CreditsView(DefaultMetaTags, TemplateView): +# template_name = "weallcode/credits.html" +# url = reverse_lazy("weallcode-credits") - title = f"Credits & Attributions | We All Code" +# title = f"Credits & Attributions | We All Code" -class StaticSitemapView(sitemaps.Sitemap): - priority = 0.5 - changefreq = "daily" +# class StaticSitemapView(sitemaps.Sitemap): +# priority = 0.5 +# changefreq = "daily" - def items(self): - return [ - "weallcode-home", - "weallcode-our-story", - "weallcode-programs", - "weallcode-programs-summer-camps", - "weallcode-team", - "weallcode-join-us", - "weallcode-privacy", - "weallcode-credits", - ] +# def items(self): +# return [ +# "weallcode-home", +# "weallcode-our-story", +# "weallcode-programs", +# "weallcode-programs-summer-camps", +# "weallcode-team", +# "weallcode-join-us", +# "weallcode-privacy", +# "weallcode-credits", +# ] - def location(self, item): - return reverse(item) +# def location(self, item): +# return reverse(item) - def lastmod(self, item): - return datetime.now() +# def lastmod(self, item): +# return datetime.now() diff --git a/weallcode/views/__init__.py b/weallcode/views/__init__.py new file mode 100644 index 00000000..26893ec9 --- /dev/null +++ b/weallcode/views/__init__.py @@ -0,0 +1,11 @@ +from .associate_board import * +from .common import * +from .credits import * +from .home import * +from .join_us import * +from .our_story import * +from .privacy import * +from .programs import * +from .programs_summer_camps import * +from .sitemap import * +from .team import * diff --git a/weallcode/views/associate_board.py b/weallcode/views/associate_board.py new file mode 100644 index 00000000..4b0032a5 --- /dev/null +++ b/weallcode/views/associate_board.py @@ -0,0 +1,12 @@ +from django.urls import reverse_lazy +from django.views.generic import TemplateView + +from .common import DefaultMetaTags + + +class AssociateBoardView(DefaultMetaTags, TemplateView): + template_name = "weallcode/associate_board.html" + url = reverse_lazy("weallcode-associate-board") + + title = f"Join our Associate Board | We All Code" + diff --git a/weallcode/views/common.py b/weallcode/views/common.py new file mode 100644 index 00000000..2cc728d9 --- /dev/null +++ b/weallcode/views/common.py @@ -0,0 +1,43 @@ +from collections import defaultdict +from datetime import datetime +from itertools import chain + +from django.conf import settings +from django.contrib import messages, sitemaps +from django.contrib.auth import get_user_model +from django.db.models import Count +from django.urls import reverse, reverse_lazy +from django.utils import timezone +from django.views.generic import FormView, TemplateView + +from meta.views import MetadataMixin +from sentry_sdk import capture_message + +from coderdojochi.models import Course, Mentor, Session + +from ..forms import ContactForm +from ..models import AssociateBoardMember, BoardMember, StaffMember + +User = get_user_model() + + +def page_not_found_view(*args, **kwargs): + print("page_not_found_view") + capture_message("Page not found!", level="error") + + return render( + request, "404.html", {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, status=404, + ) + + +class DefaultMetaTags(MetadataMixin): + title = None + description = ( + "We All Code is volunteer run nonprofit organization that teaches web, game, and app development to " + "youth ages 7 to 17 free of charge." + ) + image = "weallcode/images/photos/real-coding-skills.jpg" + twitter_card = "summary_large_image" + twitter_creator = "@weallcode" + twitter_site = "@weallcode" + diff --git a/weallcode/views/credits.py b/weallcode/views/credits.py new file mode 100644 index 00000000..77a8d473 --- /dev/null +++ b/weallcode/views/credits.py @@ -0,0 +1,11 @@ +from django.urls import reverse_lazy +from django.views.generic import TemplateView + +from .common import DefaultMetaTags + + +class CreditsView(DefaultMetaTags, TemplateView): + template_name = "weallcode/credits.html" + # url = reverse_lazy("weallcode-credits") + + title = f"Credits & Attributions | We All Code" diff --git a/weallcode/views/home.py b/weallcode/views/home.py new file mode 100644 index 00000000..ffa83402 --- /dev/null +++ b/weallcode/views/home.py @@ -0,0 +1,26 @@ +from django.urls import reverse_lazy +from django.utils import timezone +from django.views.generic import TemplateView + +from coderdojochi.models import Session + +from .common import DefaultMetaTags + + +class HomeView(DefaultMetaTags, TemplateView): + template_name = "weallcode/home.html" + url = reverse_lazy("weallcode-home") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + sessions = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") + + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": + sessions = sessions.filter(is_public=True) + + if len(sessions) > 0: + context["next_session"] = sessions[0] + + return context + diff --git a/weallcode/views/join_us.py b/weallcode/views/join_us.py new file mode 100644 index 00000000..0785f526 --- /dev/null +++ b/weallcode/views/join_us.py @@ -0,0 +1,26 @@ +from django.contrib import messages +from django.urls import reverse_lazy +from django.views.generic import FormView + +from ..forms import ContactForm +from .common import DefaultMetaTags + + +class JoinUsView(DefaultMetaTags, FormView): + template_name = "weallcode/join_us.html" + form_class = ContactForm + url = reverse_lazy("weallcode-join-us") + success_url = reverse_lazy("weallcode-join-us") + + title = f"Join Us | We All Code" + + def form_valid(self, form): + # This method is called when valid form data has been POSTed. + # It should return an HttpResponse. + form.send_email() + messages.success( + self.request, "Thank you for contacting us! We will respond as soon as possible.", + ) + + return super().form_valid(form) + diff --git a/weallcode/views/our_story.py b/weallcode/views/our_story.py new file mode 100644 index 00000000..f0222f6f --- /dev/null +++ b/weallcode/views/our_story.py @@ -0,0 +1,12 @@ +from django.urls import reverse_lazy +from django.views.generic import TemplateView + +from .common import DefaultMetaTags + + +class OurStoryView(DefaultMetaTags, TemplateView): + template_name = "weallcode/our_story.html" + url = reverse_lazy("weallcode-our-story") + + title = f"Our Story | We All Code" + diff --git a/weallcode/views/privacy.py b/weallcode/views/privacy.py new file mode 100644 index 00000000..9c00bba8 --- /dev/null +++ b/weallcode/views/privacy.py @@ -0,0 +1,11 @@ +from django.urls import reverse_lazy +from django.views.generic import TemplateView + +from .common import DefaultMetaTags + + +class PrivacyView(DefaultMetaTags, TemplateView): + template_name = "weallcode/privacy.html" + url = reverse_lazy("weallcode-privacy") + + title = f"Privacy & Terms | We All Code" diff --git a/weallcode/views/programs.py b/weallcode/views/programs.py new file mode 100644 index 00000000..5437c41c --- /dev/null +++ b/weallcode/views/programs.py @@ -0,0 +1,40 @@ +from django.urls import reverse_lazy +from django.utils import timezone +from django.views.generic import TemplateView + +from coderdojochi.models import Course, Session + +from .common import DefaultMetaTags + + +class ProgramsView(DefaultMetaTags, TemplateView): + template_name = "weallcode/programs.html" + url = reverse_lazy("weallcode-programs") + + title = f"Programs | We All Code" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + # WEEKEND CLASSES + weekend_classes = Session.objects.filter( + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.WEEKEND, + ).order_by("start_date").prefetch_related('course', 'location') + + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": + weekend_classes = weekend_classes.filter(is_public=True) + + context["weekend_classes"] = weekend_classes + + # SUMMER CAMP CLASSES + summer_camp_classes = Session.objects.filter( + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, + ).order_by("start_date").prefetch_related('course', 'location') + + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": + summer_camp_classes = summer_camp_classes.filter(is_public=True) + + context["summer_camp_classes"] = summer_camp_classes + + return context + diff --git a/weallcode/views/programs_summer_camps.py b/weallcode/views/programs_summer_camps.py new file mode 100644 index 00000000..5002a618 --- /dev/null +++ b/weallcode/views/programs_summer_camps.py @@ -0,0 +1,26 @@ +from django.urls import reverse_lazy +from django.views.generic import TemplateView + +from .common import DefaultMetaTags + + +class ProgramsSummerCampsView(DefaultMetaTags, TemplateView): + template_name = "weallcode/programs-summer-camps.html" + url = reverse_lazy("weallcode-programs-summer-camps") + + title = f"Summer Camps | We All Code" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + # SUMMER CAMP CLASSES + summer_camp_classes = Session.objects.filter( + is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, + ).order_by("start_date") + + if not self.request.user.is_authenticated or not self.request.user.role == "mentor": + summer_camp_classes = summer_camp_classes.filter(is_public=True) + + context["summer_camp_classes"] = summer_camp_classes + + return context diff --git a/weallcode/views/sitemap.py b/weallcode/views/sitemap.py new file mode 100644 index 00000000..18479976 --- /dev/null +++ b/weallcode/views/sitemap.py @@ -0,0 +1,27 @@ +from datetime import datetime + +from django.contrib import sitemaps +from django.urls import reverse + + +class StaticSitemapView(sitemaps.Sitemap): + priority = 0.5 + changefreq = "daily" + + def items(self): + return [ + "weallcode-home", + "weallcode-our-story", + "weallcode-programs", + "weallcode-programs-summer-camps", + "weallcode-team", + "weallcode-join-us", + "weallcode-privacy", + "weallcode-credits", + ] + + def location(self, item): + return reverse(item) + + def lastmod(self, item): + return datetime.now() diff --git a/weallcode/views/team.py b/weallcode/views/team.py new file mode 100644 index 00000000..595d06f9 --- /dev/null +++ b/weallcode/views/team.py @@ -0,0 +1,64 @@ +from collections import defaultdict +from itertools import chain + +from django.db.models.aggregates import Count +from django.urls import reverse_lazy +from django.views.generic import TemplateView + +from coderdojochi.models import Mentor +from weallcode.models.associate_board_member import AssociateBoardMember +from weallcode.models.board_member import BoardMember +from weallcode.models.staff import StaffMember + +from .common import DefaultMetaTags + + +class TeamView(DefaultMetaTags, TemplateView): + template_name = "weallcode/team.html" + url = reverse_lazy("weallcode-team") + + title = f"Team | We All Code" + + # Instructors + def get_instructors(self, context, volunteers): + context["instructors"] = volunteers.filter(user__groups__name__in=["Instructor"],).order_by("user__first_name") + + return context + + # Volunteers + def get_volunteers(self, context, volunteers): + all_volunteers = volunteers.annotate(session_count=Count("mentororder")).order_by( + "-user__role", "-session_count" + ) + + mentors = [] + volunteers = [] + + for volunteer in all_volunteers: + if volunteer.session_count >= 10: + mentors += [volunteer] + else: + volunteers += [volunteer] + + context["top_mentors"] = mentors + # context['other_mentors'] = mentors[8:] + context["top_volunteers"] = volunteers[0:16] + context["other_volunteers"] = volunteers[16:] + + return context + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + volunteers = Mentor.objects.select_related("user").filter( + is_active=True, is_public=True, background_check=True, avatar_approved=True, + ) + + context["staff"] = StaffMember.objects.all() + context["board"] = BoardMember.objects.get_sorted() + context["associate_board"] = AssociateBoardMember.objects.get_sorted() + context = self.get_instructors(context, volunteers) + context = self.get_volunteers(context, volunteers) + + return context + From f50368a6e2fbc1aa450a6c74f8ea717eec9c4bdb Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Wed, 13 Jan 2021 10:38:24 -0600 Subject: [PATCH 07/57] Fix mentor file location --- .../templates/coderdojochi/mentor_detail.html | 4 ++-- .../templates/coderdojochi/mentor_list.html | 2 +- coderdojochi/urls.py | 2 +- coderdojochi/views/public/__init__.py | 1 + coderdojochi/views/{ => public}/mentor.py | 14 +++----------- 5 files changed, 8 insertions(+), 15 deletions(-) rename coderdojochi/views/{ => public}/mentor.py (55%) diff --git a/coderdojochi/templates/coderdojochi/mentor_detail.html b/coderdojochi/templates/coderdojochi/mentor_detail.html index 2971fc72..783d7126 100644 --- a/coderdojochi/templates/coderdojochi/mentor_detail.html +++ b/coderdojochi/templates/coderdojochi/mentor_detail.html @@ -8,10 +8,10 @@ {% block contained_content %}
- +
diff --git a/coderdojochi/templates/coderdojochi/mentor_list.html b/coderdojochi/templates/coderdojochi/mentor_list.html index 9b46cd8a..10c52bd5 100644 --- a/coderdojochi/templates/coderdojochi/mentor_list.html +++ b/coderdojochi/templates/coderdojochi/mentor_list.html @@ -13,8 +13,8 @@

Meet Our Mentors Photo of {{ mentor.user.first_name }} {{ mentor.user.last_name }} - {{ mentor.user.first_name }} {{ mentor.user.last_name }} +

{{ mentor.user.first_name }} {{ mentor.user.last_name }}

{% endfor %}
diff --git a/coderdojochi/urls.py b/coderdojochi/urls.py index 1105992b..e573ea9c 100644 --- a/coderdojochi/urls.py +++ b/coderdojochi/urls.py @@ -20,7 +20,7 @@ meeting_announce, meeting_sign_up, ) -from .views.mentor import MentorDetailView, MentorListView +from .views.public import MentorDetailView, MentorListView admin.autodiscover() diff --git a/coderdojochi/views/public/__init__.py b/coderdojochi/views/public/__init__.py index 494eed44..3d4ea842 100644 --- a/coderdojochi/views/public/__init__.py +++ b/coderdojochi/views/public/__init__.py @@ -1 +1,2 @@ +from .mentor import * from .sessions import * diff --git a/coderdojochi/views/mentor.py b/coderdojochi/views/public/mentor.py similarity index 55% rename from coderdojochi/views/mentor.py rename to coderdojochi/views/public/mentor.py index 732aee63..1a7f606c 100644 --- a/coderdojochi/views/mentor.py +++ b/coderdojochi/views/public/mentor.py @@ -1,6 +1,6 @@ from django.views.generic import DetailView, ListView -from ..models import Mentor +from ...models import Mentor class MentorListView(ListView): @@ -9,11 +9,7 @@ class MentorListView(ListView): def get_queryset(self): return ( Mentor.objects.filter( - user__is_active=True, - is_active=True, - is_public=True, - background_check=True, - avatar_approved=True, + user__is_active=True, is_active=True, is_public=True, background_check=True, avatar_approved=True, ) .select_related("user") .order_by("user__date_joined") @@ -25,9 +21,5 @@ class MentorDetailView(DetailView): def get_queryset(self): return Mentor.objects.filter( - user__is_active=True, - is_active=True, - is_public=True, - background_check=True, - avatar_approved=True, + user__is_active=True, is_active=True, is_public=True, background_check=True, avatar_approved=True, ).select_related("user") From 15eccd1582751090209a0865aabe30cdc643d15c Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Wed, 13 Jan 2021 10:56:47 -0600 Subject: [PATCH 08/57] Run black 20.8b1 --- accounts/views.py | 35 ++-- coderdojochi/admin.py | 67 ++++++-- coderdojochi/forms.py | 60 +++++-- coderdojochi/models/common.py | 9 +- coderdojochi/models/course.py | 48 ++++-- coderdojochi/models/donation.py | 51 ++++-- coderdojochi/models/email_content.py | 24 ++- coderdojochi/models/equipment.py | 82 +++++++--- coderdojochi/models/guardian.py | 42 +++-- coderdojochi/models/location.py | 42 +++-- coderdojochi/models/meeting.py | 146 +++++++++++++---- coderdojochi/models/mentor.py | 72 +++++++-- coderdojochi/models/mentor_order.py | 45 ++++-- coderdojochi/models/order.py | 57 +++++-- coderdojochi/models/race_ethnicity.py | 8 +- coderdojochi/models/session.py | 152 ++++++++++++++---- coderdojochi/models/student.py | 51 ++++-- coderdojochi/models/user.py | 14 +- coderdojochi/old_views.py | 49 ++++-- coderdojochi/settings.py | 21 ++- .../templatetags/coderdojochi_extras.py | 8 +- coderdojochi/urls.py | 18 ++- coderdojochi/views/guardian/sessions.py | 1 - coderdojochi/views/meetings.py | 5 +- coderdojochi/views/mentor/sessions.py | 1 - coderdojochi/views/profile.py | 5 +- coderdojochi/views/public/mentor.py | 12 +- coderdojochi/views/public/sessions.py | 1 - coderdojochi/views/sessions.py | 22 ++- tasks.py | 14 +- weallcode/forms.py | 37 ++++- weallcode/models/associate_board_member.py | 6 +- weallcode/models/board_member.py | 7 +- weallcode/models/common.py | 40 ++++- weallcode/models/staff.py | 11 +- weallcode/views/associate_board.py | 1 - weallcode/views/common.py | 9 +- weallcode/views/home.py | 1 - weallcode/views/join_us.py | 4 +- weallcode/views/our_story.py | 1 - weallcode/views/programs.py | 25 ++- weallcode/views/programs_summer_camps.py | 4 +- weallcode/views/team.py | 10 +- 43 files changed, 1031 insertions(+), 287 deletions(-) diff --git a/accounts/views.py b/accounts/views.py index 59008d31..0d090aa3 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -50,7 +50,8 @@ def dispatch(self, *args, **kwargs): return redirect(f"{reverse('welcome')}?next={self.request.GET['next']}") else: messages.warning( - self.request, "Tell us a little about yourself before going on account.", + self.request, + "Tell us a little about yourself before going on account.", ) return redirect("welcome") @@ -77,7 +78,10 @@ def get_context_data(self, **kwargs): def get_context_data_for_mentor(self): mentor = get_object_or_404(Mentor, user=self.request.user) - orders = MentorOrder.objects.select_related().filter(is_active=True, mentor=mentor,) + orders = MentorOrder.objects.select_related().filter( + is_active=True, + mentor=mentor, + ) upcoming_sessions = orders.filter(is_active=True, session__start_date__gte=timezone.now()).order_by( "session__start_date" @@ -90,7 +94,9 @@ def get_context_data_for_mentor(self): meeting_orders = MeetingOrder.objects.select_related().filter(mentor=mentor) upcoming_meetings = meeting_orders.filter( - is_active=True, meeting__is_public=True, meeting__end_date__gte=timezone.now(), + is_active=True, + meeting__is_public=True, + meeting__end_date__gte=timezone.now(), ).order_by("meeting__start_date") account_complete = False @@ -117,18 +123,25 @@ def get_context_data_for_mentor(self): def get_context_data_for_guardian(self): guardian = get_object_or_404(Guardian, user=self.request.user) - students = Student.objects.filter(is_active=True, guardian=guardian,) - - student_orders = Order.objects.filter(student__in=students,) - - upcoming_orders = student_orders.filter(is_active=True, session__start_date__gte=timezone.now(),).order_by( - "session__start_date" + students = Student.objects.filter( + is_active=True, + guardian=guardian, ) - past_orders = student_orders.filter(is_active=True, session__start_date__lte=timezone.now(),).order_by( - "session__start_date" + student_orders = Order.objects.filter( + student__in=students, ) + upcoming_orders = student_orders.filter( + is_active=True, + session__start_date__gte=timezone.now(), + ).order_by("session__start_date") + + past_orders = student_orders.filter( + is_active=True, + session__start_date__lte=timezone.now(), + ).order_by("session__start_date") + return { "guardian": guardian, "students": students, diff --git a/coderdojochi/admin.py b/coderdojochi/admin.py index f1ea6d3f..2c95f65d 100644 --- a/coderdojochi/admin.py +++ b/coderdojochi/admin.py @@ -157,7 +157,16 @@ def get_queryset(self, request): qs = super(MentorAdmin, self).get_queryset(request) # Count all orders that are marked as active - qs = qs.annotate(mentororder__count=Count(Case(When(mentororder__is_active=True, then=1,)))) + qs = qs.annotate( + mentororder__count=Count( + Case( + When( + mentororder__is_active=True, + then=1, + ) + ) + ) + ) return qs def mentor_count_link(self, obj): @@ -303,15 +312,42 @@ def student_count_link(self, obj): class StudentResource(resources.ModelResource): - first_name = Field(attribute="first_name", column_name="first_name",) - last_name = Field(attribute="last_name", column_name="last_name",) - guardian_email = Field(attribute="guardian_email", column_name="guardian_email",) - birthday = Field(attribute="birthday", column_name="birthday",) - gender = Field(attribute="gender", column_name="gender",) - school_name = Field(attribute="school_name", column_name="school_name",) - school_type = Field(attribute="school_type", column_name="school_type",) - photo_release = Field(attribute="photo_release", column_name="photo_release",) - consent = Field(attribute="consent", column_name="consent",) + first_name = Field( + attribute="first_name", + column_name="first_name", + ) + last_name = Field( + attribute="last_name", + column_name="last_name", + ) + guardian_email = Field( + attribute="guardian_email", + column_name="guardian_email", + ) + birthday = Field( + attribute="birthday", + column_name="birthday", + ) + gender = Field( + attribute="gender", + column_name="gender", + ) + school_name = Field( + attribute="school_name", + column_name="school_name", + ) + school_type = Field( + attribute="school_type", + column_name="school_type", + ) + photo_release = Field( + attribute="photo_release", + column_name="photo_release", + ) + consent = Field( + attribute="consent", + column_name="consent", + ) def import_obj(self, obj, data, dry_run): guardian_email = data.get("guardian_email") @@ -402,7 +438,16 @@ def get_queryset(self, request): qs = qs.select_related() # Count all orders that are marked as active - qs = qs.annotate(order__count=Count(Case(When(order__is_active=True, then=1,)))) + qs = qs.annotate( + order__count=Count( + Case( + When( + order__is_active=True, + then=1, + ) + ) + ) + ) return qs def guardian_link(self, obj): diff --git a/coderdojochi/forms.py b/coderdojochi/forms.py index 4e98297d..425ea19d 100644 --- a/coderdojochi/forms.py +++ b/coderdojochi/forms.py @@ -201,25 +201,55 @@ class StudentForm(CDCModelForm): ] first_name = forms.CharField( - widget=forms.TextInput(attrs={"placeholder": "Jane", "class": "form-control",},), label="First Name", + widget=forms.TextInput( + attrs={ + "placeholder": "Jane", + "class": "form-control", + }, + ), + label="First Name", ) last_name = forms.CharField( - widget=forms.TextInput(attrs={"placeholder": "Doe", "class": "form-control",},), label="Last Name", + widget=forms.TextInput( + attrs={ + "placeholder": "Doe", + "class": "form-control", + }, + ), + label="Last Name", ) gender = forms.CharField( - widget=forms.TextInput(attrs={"placeholder": "", "class": "form-control",},), label="Gender", + widget=forms.TextInput( + attrs={ + "placeholder": "", + "class": "form-control", + }, + ), + label="Gender", ) school_name = forms.CharField( - widget=forms.TextInput(attrs={"class": "form-control",},), label="School Name", required=False, + widget=forms.TextInput( + attrs={ + "class": "form-control", + }, + ), + label="School Name", + required=False, ) - school_type = forms.ChoiceField(widget=forms.RadioSelect, choices=SCHOOL_TYPE_CHOICES, required=False,) + school_type = forms.ChoiceField( + widget=forms.RadioSelect, + choices=SCHOOL_TYPE_CHOICES, + required=False, + ) race_ethnicity = forms.ModelMultipleChoiceField( - widget=forms.CheckboxSelectMultiple, queryset=RaceEthnicity.objects.filter(is_visible=True), required=False, + widget=forms.CheckboxSelectMultiple, + queryset=RaceEthnicity.objects.filter(is_visible=True), + required=False, ) birthday = forms.CharField( @@ -250,7 +280,11 @@ class StudentForm(CDCModelForm): medical_conditions = forms.CharField( widget=forms.Textarea( - attrs={"placeholder": "List any medical conditions.", "class": "form-control hidden", "rows": 5,}, + attrs={ + "placeholder": "List any medical conditions.", + "class": "form-control hidden", + "rows": 5, + }, ), label=format_html( "{0} {1}", @@ -261,7 +295,11 @@ class StudentForm(CDCModelForm): ) photo_release = forms.BooleanField( - widget=forms.CheckboxInput(attrs={"required": "required",},), + widget=forms.CheckboxInput( + attrs={ + "required": "required", + }, + ), label=( "I hereby give permission to We All Code to use the " "student's image and/or likeness in promotional materials." @@ -269,7 +307,11 @@ class StudentForm(CDCModelForm): ) consent = forms.BooleanField( - widget=forms.CheckboxInput(attrs={"required": "required",},), + widget=forms.CheckboxInput( + attrs={ + "required": "required", + }, + ), label=( "I hereby give consent for the student signed up above to participate in We All Code as per the " f"terms." diff --git a/coderdojochi/models/common.py b/coderdojochi/models/common.py index 3f561051..40464977 100644 --- a/coderdojochi/models/common.py +++ b/coderdojochi/models/common.py @@ -3,10 +3,13 @@ class CommonInfo(models.Model): # Auto create/update - created_at = models.DateTimeField(auto_now_add=True,) + created_at = models.DateTimeField( + auto_now_add=True, + ) - updated_at = models.DateTimeField(auto_now=True,) + updated_at = models.DateTimeField( + auto_now=True, + ) class Meta: abstract = True - diff --git a/coderdojochi/models/course.py b/coderdojochi/models/course.py index f4a32b46..d7431993 100644 --- a/coderdojochi/models/course.py +++ b/coderdojochi/models/course.py @@ -18,15 +18,45 @@ class Course(CommonInfo): (SPECIAL, "Special"), ] - code = models.CharField(max_length=255, blank=True, null=True,) - course_type = models.CharField("type", max_length=2, choices=COURSE_TYPE_CHOICES, default=WEEKEND,) - title = models.CharField(max_length=255,) - slug = models.SlugField(max_length=40, blank=True, null=True,) - description = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - duration = models.DurationField(default=timedelta(hours=3), help_text="HH:MM:ss",) - minimum_age = models.IntegerField(default=7, validators=[MinValueValidator(0), MaxValueValidator(100)],) - maximum_age = models.IntegerField(default=17, validators=[MinValueValidator(0), MaxValueValidator(100)],) - is_active = models.BooleanField(default=True,) + code = models.CharField( + max_length=255, + blank=True, + null=True, + ) + course_type = models.CharField( + "type", + max_length=2, + choices=COURSE_TYPE_CHOICES, + default=WEEKEND, + ) + title = models.CharField( + max_length=255, + ) + slug = models.SlugField( + max_length=40, + blank=True, + null=True, + ) + description = models.TextField( + blank=True, + null=True, + help_text="Basic HTML allowed", + ) + duration = models.DurationField( + default=timedelta(hours=3), + help_text="HH:MM:ss", + ) + minimum_age = models.IntegerField( + default=7, + validators=[MinValueValidator(0), MaxValueValidator(100)], + ) + maximum_age = models.IntegerField( + default=17, + validators=[MinValueValidator(0), MaxValueValidator(100)], + ) + is_active = models.BooleanField( + default=True, + ) def __str__(self): if self.code: diff --git a/coderdojochi/models/donation.py b/coderdojochi/models/donation.py index 173791ec..6478c630 100644 --- a/coderdojochi/models/donation.py +++ b/coderdojochi/models/donation.py @@ -9,22 +9,54 @@ class Donation(CommonInfo): from .session import Session from .user import CDCUser - user = models.ForeignKey(CDCUser, blank=True, null=True, on_delete=models.CASCADE,) - session = models.ForeignKey(Session, blank=True, null=True, on_delete=models.CASCADE,) - first_name = models.CharField(max_length=255, blank=True, null=True,) - last_name = models.CharField(max_length=255, blank=True, null=True,) - referral_code = models.CharField(max_length=255, blank=True, null=True,) - email = models.EmailField(blank=True, null=True,) + user = models.ForeignKey( + CDCUser, + blank=True, + null=True, + on_delete=models.CASCADE, + ) + session = models.ForeignKey( + Session, + blank=True, + null=True, + on_delete=models.CASCADE, + ) + first_name = models.CharField( + max_length=255, + blank=True, + null=True, + ) + last_name = models.CharField( + max_length=255, + blank=True, + null=True, + ) + referral_code = models.CharField( + max_length=255, + blank=True, + null=True, + ) + email = models.EmailField( + blank=True, + null=True, + ) amount = models.IntegerField() - is_verified = models.BooleanField(default=False,) - receipt_sent = models.BooleanField(default=False,) + is_verified = models.BooleanField( + default=False, + ) + receipt_sent = models.BooleanField( + default=False, + ) def __str__(self): return f"{self.email} | ${self.amount}" def get_admin_url(self): content_type = ContentType.objects.get_for_model(self.__class__) - return reverse(f"admin:{content_type.app_label}_{content_type.model}_change", args=(self.id,),) + return reverse( + f"admin:{content_type.app_label}_{content_type.model}_change", + args=(self.id,), + ) def get_first_name(self): if self.user: @@ -49,4 +81,3 @@ def get_email(self): return self.email get_email.short_description = "Email" - diff --git a/coderdojochi/models/email_content.py b/coderdojochi/models/email_content.py index ff59f457..7d8f455a 100644 --- a/coderdojochi/models/email_content.py +++ b/coderdojochi/models/email_content.py @@ -5,13 +5,23 @@ class EmailContent(CommonInfo): - nickname = models.CharField(max_length=255,) - - subject = models.CharField(max_length=255,) - - body = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - - is_active = models.BooleanField(default=True,) + nickname = models.CharField( + max_length=255, + ) + + subject = models.CharField( + max_length=255, + ) + + body = models.TextField( + blank=True, + null=True, + help_text="Basic HTML allowed", + ) + + is_active = models.BooleanField( + default=True, + ) class Meta: verbose_name = "email content" diff --git a/coderdojochi/models/equipment.py b/coderdojochi/models/equipment.py index 397ea573..2e0b6eb4 100644 --- a/coderdojochi/models/equipment.py +++ b/coderdojochi/models/equipment.py @@ -5,7 +5,11 @@ class EquipmentType(CommonInfo): - name = models.CharField(max_length=255, blank=False, null=False,) + name = models.CharField( + max_length=255, + blank=False, + null=False, + ) def __str__(self): return self.name @@ -21,27 +25,61 @@ class Equipment(CommonInfo): (UNUSABLE, "Unusable"), ] - uuid = models.CharField(max_length=255, verbose_name="UUID", default="000-000-000-000", null=False,) - - equipment_type = models.ForeignKey(EquipmentType, on_delete=models.CASCADE,) - - make = models.CharField(max_length=255,) - - model = models.CharField(max_length=255,) - - asset_tag = models.CharField(max_length=255,) - - acquisition_date = models.DateTimeField(blank=True, null=True,) - - condition = models.CharField(max_length=255, choices=EQUIPMENT_CONDITIONS,) - - notes = models.TextField(blank=True, null=True,) - - last_system_update_check_in = models.DateTimeField(blank=True, null=True, verbose_name="Last Check In",) - - last_system_update = models.DateTimeField(blank=True, null=True, verbose_name="Last Update",) - - force_update_on_next_boot = models.BooleanField(default=False, verbose_name="Force Update",) + uuid = models.CharField( + max_length=255, + verbose_name="UUID", + default="000-000-000-000", + null=False, + ) + + equipment_type = models.ForeignKey( + EquipmentType, + on_delete=models.CASCADE, + ) + + make = models.CharField( + max_length=255, + ) + + model = models.CharField( + max_length=255, + ) + + asset_tag = models.CharField( + max_length=255, + ) + + acquisition_date = models.DateTimeField( + blank=True, + null=True, + ) + + condition = models.CharField( + max_length=255, + choices=EQUIPMENT_CONDITIONS, + ) + + notes = models.TextField( + blank=True, + null=True, + ) + + last_system_update_check_in = models.DateTimeField( + blank=True, + null=True, + verbose_name="Last Check In", + ) + + last_system_update = models.DateTimeField( + blank=True, + null=True, + verbose_name="Last Update", + ) + + force_update_on_next_boot = models.BooleanField( + default=False, + verbose_name="Force Update", + ) class Meta: verbose_name = "equipment" diff --git a/coderdojochi/models/guardian.py b/coderdojochi/models/guardian.py index 4d1b4683..5db3c2eb 100644 --- a/coderdojochi/models/guardian.py +++ b/coderdojochi/models/guardian.py @@ -6,13 +6,35 @@ class Guardian(CommonInfo): - user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - is_active = models.BooleanField(default=True,) - phone = models.CharField(max_length=50, blank=True,) - zip = models.CharField(max_length=20, blank=True, null=True,) - birthday = models.DateTimeField(blank=False, null=True,) - gender = models.CharField(max_length=255, blank=False, null=True,) - race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=False,) + user = models.ForeignKey( + CDCUser, + on_delete=models.CASCADE, + ) + is_active = models.BooleanField( + default=True, + ) + phone = models.CharField( + max_length=50, + blank=True, + ) + zip = models.CharField( + max_length=20, + blank=True, + null=True, + ) + birthday = models.DateTimeField( + blank=False, + null=True, + ) + gender = models.CharField( + max_length=255, + blank=False, + null=True, + ) + race_ethnicity = models.ManyToManyField( + RaceEthnicity, + blank=False, + ) def __str__(self): return f"{self.user.first_name} {self.user.last_name}" @@ -32,5 +54,7 @@ def email(self): def get_students(self): from .student import Student - return Student.objects.filter(guardian=self, is_active=True,) - + return Student.objects.filter( + guardian=self, + is_active=True, + ) diff --git a/coderdojochi/models/location.py b/coderdojochi/models/location.py index 905f6e0b..1a7f9ad5 100644 --- a/coderdojochi/models/location.py +++ b/coderdojochi/models/location.py @@ -5,17 +5,37 @@ class Location(CommonInfo): - name = models.CharField(max_length=255,) - - address = models.CharField(blank=True, null=True, max_length=255,) - - city = models.CharField(blank=True, null=True, max_length=255,) - - state = models.CharField(blank=True, null=True, max_length=2,) - - zip = models.CharField(blank=True, null=True, max_length=20,) - - is_active = models.BooleanField(default=True,) + name = models.CharField( + max_length=255, + ) + + address = models.CharField( + blank=True, + null=True, + max_length=255, + ) + + city = models.CharField( + blank=True, + null=True, + max_length=255, + ) + + state = models.CharField( + blank=True, + null=True, + max_length=2, + ) + + zip = models.CharField( + blank=True, + null=True, + max_length=20, + ) + + is_active = models.BooleanField( + default=True, + ) class Meta: ordering = ["-id"] diff --git a/coderdojochi/models/meeting.py b/coderdojochi/models/meeting.py index 4cadc09d..9fce32ce 100644 --- a/coderdojochi/models/meeting.py +++ b/coderdojochi/models/meeting.py @@ -8,10 +8,24 @@ class MeetingType(CommonInfo): - code = models.CharField(max_length=255, blank=True, null=True,) - title = models.CharField(max_length=255,) - slug = models.SlugField(max_length=40, blank=True, null=True,) - description = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) + code = models.CharField( + max_length=255, + blank=True, + null=True, + ) + title = models.CharField( + max_length=255, + ) + slug = models.SlugField( + max_length=40, + blank=True, + null=True, + ) + description = models.TextField( + blank=True, + null=True, + help_text="Basic HTML allowed", + ) def __str__(self): if self.code: @@ -21,19 +35,52 @@ def __str__(self): class Meeting(CommonInfo): - meeting_type = models.ForeignKey(MeetingType, on_delete=models.CASCADE,) - additional_info = models.TextField(blank=True, null=True, help_text="Basic HTML allowed",) - start_date = models.DateTimeField(blank=True, null=True,) - end_date = models.DateTimeField(blank=True, null=True,) - location = models.ForeignKey(Location, on_delete=models.CASCADE,) + meeting_type = models.ForeignKey( + MeetingType, + on_delete=models.CASCADE, + ) + additional_info = models.TextField( + blank=True, + null=True, + help_text="Basic HTML allowed", + ) + start_date = models.DateTimeField( + blank=True, + null=True, + ) + end_date = models.DateTimeField( + blank=True, + null=True, + ) + location = models.ForeignKey( + Location, + on_delete=models.CASCADE, + ) external_enrollment_url = models.CharField( - max_length=255, blank=True, null=True, help_text="When provided, local enrollment is disabled.", + max_length=255, + blank=True, + null=True, + help_text="When provided, local enrollment is disabled.", + ) + is_public = models.BooleanField( + default=False, + ) + is_active = models.BooleanField( + default=False, + ) + image_url = models.CharField( + max_length=255, + blank=True, + null=True, + ) + bg_image = models.ImageField( + blank=True, + null=True, + ) + announced_date = models.DateTimeField( + blank=True, + null=True, ) - is_public = models.BooleanField(default=False,) - is_active = models.BooleanField(default=False,) - image_url = models.CharField(max_length=255, blank=True, null=True,) - bg_image = models.ImageField(blank=True, null=True,) - announced_date = models.DateTimeField(blank=True, null=True,) def __str__(self): date = formats.date_format(self.start_date, "SHORT_DATETIME_FORMAT") @@ -52,25 +99,35 @@ def get_current_orders(self, checked_in=None): if checked_in is not None: if checked_in: orders = ( - MeetingOrder.objects.filter(is_active=True, meeting=self,) - .exclude(check_in=None,) + MeetingOrder.objects.filter( + is_active=True, + meeting=self, + ) + .exclude( + check_in=None, + ) .order_by("mentor__user__last_name") ) else: - orders = MeetingOrder.objects.filter(is_active=True, meeting=self, check_in=None,).order_by( - "mentor__user__last_name" - ) + orders = MeetingOrder.objects.filter( + is_active=True, + meeting=self, + check_in=None, + ).order_by("mentor__user__last_name") else: orders = MeetingOrder.objects.filter(is_active=True, meeting=self,).order_by( - "check_in", "mentor__user__last_name", + "check_in", + "mentor__user__last_name", ) return orders def get_current_mentors(self): return Mentor.objects.filter( - id__in=MeetingOrder.objects.filter(is_active=True, meeting=self,).values("mentor__id",) + id__in=MeetingOrder.objects.filter(is_active=True, meeting=self,).values( + "mentor__id", + ) ) def get_mentor_count(self): @@ -80,15 +137,42 @@ def get_mentor_count(self): class MeetingOrder(CommonInfo): - mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE,) - meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE,) - is_active = models.BooleanField(default=True,) - ip = models.CharField(max_length=255, blank=True, null=True,) - check_in = models.DateTimeField(blank=True, null=True,) - affiliate = models.CharField(max_length=255, blank=True, null=True,) - order_number = models.CharField(max_length=255, blank=True, null=True,) - week_reminder_sent = models.BooleanField(default=False,) - day_reminder_sent = models.BooleanField(default=False,) + mentor = models.ForeignKey( + Mentor, + on_delete=models.CASCADE, + ) + meeting = models.ForeignKey( + Meeting, + on_delete=models.CASCADE, + ) + is_active = models.BooleanField( + default=True, + ) + ip = models.CharField( + max_length=255, + blank=True, + null=True, + ) + check_in = models.DateTimeField( + blank=True, + null=True, + ) + affiliate = models.CharField( + max_length=255, + blank=True, + null=True, + ) + order_number = models.CharField( + max_length=255, + blank=True, + null=True, + ) + week_reminder_sent = models.BooleanField( + default=False, + ) + day_reminder_sent = models.BooleanField( + default=False, + ) def __str__(self): return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.meeting.meeting_type.title}" diff --git a/coderdojochi/models/mentor.py b/coderdojochi/models/mentor.py index cf7ce632..99ff29bd 100644 --- a/coderdojochi/models/mentor.py +++ b/coderdojochi/models/mentor.py @@ -19,21 +19,65 @@ def generate_filename(instance, filename): # TODO: Add MentorManager class Mentor(CommonInfo): - user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - bio = models.TextField(blank=True, null=True,) - is_active = models.BooleanField(default=True,) - background_check = models.BooleanField(default=False,) - is_public = models.BooleanField(default=False,) + user = models.ForeignKey( + CDCUser, + on_delete=models.CASCADE, + ) + bio = models.TextField( + blank=True, + null=True, + ) + is_active = models.BooleanField( + default=True, + ) + background_check = models.BooleanField( + default=False, + ) + is_public = models.BooleanField( + default=False, + ) avatar = StdImageField( - upload_to=generate_filename, blank=True, variations={"thumbnail": {"width": 500, "height": 500, "crop": True,}}, + upload_to=generate_filename, + blank=True, + variations={ + "thumbnail": { + "width": 500, + "height": 500, + "crop": True, + } + }, + ) + avatar_approved = models.BooleanField( + default=False, + ) + birthday = models.DateTimeField( + blank=False, + null=True, + ) + gender = models.CharField( + max_length=255, + blank=False, + null=True, + ) + race_ethnicity = models.ManyToManyField( + RaceEthnicity, + blank=False, + ) + work_place = models.CharField( + max_length=255, + blank=True, + null=True, + ) + phone = models.CharField( + max_length=255, + blank=True, + null=True, + ) + home_address = models.CharField( + max_length=255, + blank=True, + null=True, ) - avatar_approved = models.BooleanField(default=False,) - birthday = models.DateTimeField(blank=False, null=True,) - gender = models.CharField(max_length=255, blank=False, null=True,) - race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=False,) - work_place = models.CharField(max_length=255, blank=True, null=True,) - phone = models.CharField(max_length=255, blank=True, null=True,) - home_address = models.CharField(max_length=255, blank=True, null=True,) def __str__(self): return f"{self.user.first_name} {self.user.last_name}" @@ -70,5 +114,3 @@ def first_name(self): @property def last_name(self): return self.user.last_name - - diff --git a/coderdojochi/models/mentor_order.py b/coderdojochi/models/mentor_order.py index 1fe0494d..076d83ae 100644 --- a/coderdojochi/models/mentor_order.py +++ b/coderdojochi/models/mentor_order.py @@ -10,15 +10,42 @@ class MentorOrder(CommonInfo): from .mentor import Mentor from .session import Session - mentor = models.ForeignKey(Mentor, on_delete=models.CASCADE,) - session = models.ForeignKey(Session, on_delete=models.CASCADE,) - is_active = models.BooleanField(default=True,) - ip = models.CharField(max_length=255, blank=True, null=True,) - check_in = models.DateTimeField(blank=True, null=True,) - affiliate = models.CharField(max_length=255, blank=True, null=True,) - order_number = models.CharField(max_length=255, blank=True, null=True,) - week_reminder_sent = models.BooleanField(default=False,) - day_reminder_sent = models.BooleanField(default=False,) + mentor = models.ForeignKey( + Mentor, + on_delete=models.CASCADE, + ) + session = models.ForeignKey( + Session, + on_delete=models.CASCADE, + ) + is_active = models.BooleanField( + default=True, + ) + ip = models.CharField( + max_length=255, + blank=True, + null=True, + ) + check_in = models.DateTimeField( + blank=True, + null=True, + ) + affiliate = models.CharField( + max_length=255, + blank=True, + null=True, + ) + order_number = models.CharField( + max_length=255, + blank=True, + null=True, + ) + week_reminder_sent = models.BooleanField( + default=False, + ) + day_reminder_sent = models.BooleanField( + default=False, + ) def __str__(self): return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.session.course.title}" diff --git a/coderdojochi/models/order.py b/coderdojochi/models/order.py index 629e5959..fd54107f 100644 --- a/coderdojochi/models/order.py +++ b/coderdojochi/models/order.py @@ -8,17 +8,51 @@ class Order(CommonInfo): from .session import Session from .student import Student - guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE,) - session = models.ForeignKey(Session, on_delete=models.CASCADE,) - student = models.ForeignKey(Student, on_delete=models.CASCADE,) - is_active = models.BooleanField(default=True,) - ip = models.CharField(max_length=255, blank=True, null=True,) - check_in = models.DateTimeField(blank=True, null=True,) - alternate_guardian = models.CharField(max_length=255, blank=True, null=True,) - affiliate = models.CharField(max_length=255, blank=True, null=True,) - order_number = models.CharField(max_length=255, blank=True, null=True,) - week_reminder_sent = models.BooleanField(default=False,) - day_reminder_sent = models.BooleanField(default=False,) + guardian = models.ForeignKey( + Guardian, + on_delete=models.CASCADE, + ) + session = models.ForeignKey( + Session, + on_delete=models.CASCADE, + ) + student = models.ForeignKey( + Student, + on_delete=models.CASCADE, + ) + is_active = models.BooleanField( + default=True, + ) + ip = models.CharField( + max_length=255, + blank=True, + null=True, + ) + check_in = models.DateTimeField( + blank=True, + null=True, + ) + alternate_guardian = models.CharField( + max_length=255, + blank=True, + null=True, + ) + affiliate = models.CharField( + max_length=255, + blank=True, + null=True, + ) + order_number = models.CharField( + max_length=255, + blank=True, + null=True, + ) + week_reminder_sent = models.BooleanField( + default=False, + ) + day_reminder_sent = models.BooleanField( + default=False, + ) def __str__(self): return f"{self.student.first_name} {self.student.last_name} | {self.session.course.title}" @@ -37,4 +71,3 @@ def get_student_gender(self): return self.student.get_clean_gender().title() get_student_gender.short_description = "Gender" - diff --git a/coderdojochi/models/race_ethnicity.py b/coderdojochi/models/race_ethnicity.py index 3ca96352..ec86ca56 100644 --- a/coderdojochi/models/race_ethnicity.py +++ b/coderdojochi/models/race_ethnicity.py @@ -4,8 +4,12 @@ class RaceEthnicity(CommonInfo): - race_ethnicity = models.CharField(max_length=255,) - is_visible = models.BooleanField(default=False,) + race_ethnicity = models.CharField( + max_length=255, + ) + is_visible = models.BooleanField( + default=False, + ) class Meta: verbose_name = "Race/Ethnicity" diff --git a/coderdojochi/models/session.py b/coderdojochi/models/session.py index d2a599f2..b49ec70e 100644 --- a/coderdojochi/models/session.py +++ b/coderdojochi/models/session.py @@ -23,11 +23,24 @@ class Session(CommonInfo): (FEMALE, "Female"), ) - course = models.ForeignKey(Course, on_delete=models.CASCADE, limit_choices_to={"is_active": True},) + course = models.ForeignKey( + Course, + on_delete=models.CASCADE, + limit_choices_to={"is_active": True}, + ) start_date = models.DateTimeField() - location = models.ForeignKey(Location, on_delete=models.CASCADE, limit_choices_to={"is_active": True},) - capacity = models.IntegerField(default=20,) - mentor_capacity = models.IntegerField(blank=True, null=True,) + location = models.ForeignKey( + Location, + on_delete=models.CASCADE, + limit_choices_to={"is_active": True}, + ) + capacity = models.IntegerField( + default=20, + ) + mentor_capacity = models.IntegerField( + blank=True, + null=True, + ) instructor = models.ForeignKey( Mentor, on_delete=models.CASCADE, @@ -36,28 +49,82 @@ class Session(CommonInfo): ) # Pricing - cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) - minimum_cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) - maximum_cost = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True,) + cost = models.DecimalField( + max_digits=6, + decimal_places=2, + blank=True, + null=True, + ) + minimum_cost = models.DecimalField( + max_digits=6, + decimal_places=2, + blank=True, + null=True, + ) + maximum_cost = models.DecimalField( + max_digits=6, + decimal_places=2, + blank=True, + null=True, + ) # Extra additional_info = models.TextField(blank=True, null=True, help_text="Basic HTML allowed") - waitlist_mentors = models.ManyToManyField(Mentor, blank=True, related_name="session_waitlist_mentors",) - waitlist_students = models.ManyToManyField(Student, blank=True, related_name="session_waitlist_students",) + waitlist_mentors = models.ManyToManyField( + Mentor, + blank=True, + related_name="session_waitlist_mentors", + ) + waitlist_students = models.ManyToManyField( + Student, + blank=True, + related_name="session_waitlist_students", + ) external_enrollment_url = models.CharField( - max_length=255, blank=True, null=True, help_text="When provided, local enrollment is disabled.", - ) - - is_active = models.BooleanField(default=False, help_text="Session is active.",) - is_public = models.BooleanField(default=False, help_text="Session is a public session.",) - password = models.CharField(blank=True, max_length=255,) - partner_message = models.TextField(blank=True,) - announced_date_mentors = models.DateTimeField(blank=True, null=True,) - announced_date_guardians = models.DateTimeField(blank=True, null=True,) - image_url = models.CharField(max_length=255, blank=True, null=True,) - bg_image = models.ImageField(blank=True, null=True,) - mentors_week_reminder_sent = models.BooleanField(default=False,) - mentors_day_reminder_sent = models.BooleanField(default=False,) + max_length=255, + blank=True, + null=True, + help_text="When provided, local enrollment is disabled.", + ) + + is_active = models.BooleanField( + default=False, + help_text="Session is active.", + ) + is_public = models.BooleanField( + default=False, + help_text="Session is a public session.", + ) + password = models.CharField( + blank=True, + max_length=255, + ) + partner_message = models.TextField( + blank=True, + ) + announced_date_mentors = models.DateTimeField( + blank=True, + null=True, + ) + announced_date_guardians = models.DateTimeField( + blank=True, + null=True, + ) + image_url = models.CharField( + max_length=255, + blank=True, + null=True, + ) + bg_image = models.ImageField( + blank=True, + null=True, + ) + mentors_week_reminder_sent = models.BooleanField( + default=False, + ) + mentors_day_reminder_sent = models.BooleanField( + default=False, + ) gender_limitation = models.CharField( help_text="Limits the class to be only one gender.", max_length=255, @@ -80,10 +147,17 @@ class Session(CommonInfo): validators=[MinValueValidator(0), MaxValueValidator(100)], ) online_video_link = models.URLField( - "Online Video Link", help_text="Zoom link with password.", blank=True, null=True, + "Online Video Link", + help_text="Zoom link with password.", + blank=True, + null=True, ) online_video_meeting_id = models.CharField( - "Online Video Meeting ID", help_text="XXX XXXX XXXX", max_length=255, blank=True, null=True, + "Online Video Meeting ID", + help_text="XXX XXXX XXXX", + max_length=255, + blank=True, + null=True, ) online_video_meeting_password = models.CharField( "Online Video Meeting Password", @@ -100,9 +174,18 @@ class Session(CommonInfo): ) # kept for older records - old_end_date = models.DateTimeField(blank=True, null=True,) - old_mentor_start_date = models.DateTimeField(blank=True, null=True,) - old_mentor_end_date = models.DateTimeField(blank=True, null=True,) + old_end_date = models.DateTimeField( + blank=True, + null=True, + ) + old_mentor_start_date = models.DateTimeField( + blank=True, + null=True, + ) + old_mentor_end_date = models.DateTimeField( + blank=True, + null=True, + ) @property def end_date(self): @@ -177,7 +260,10 @@ def is_guardian_announced(self): def get_mentor_orders(self): from .mentor_order import MentorOrder - return MentorOrder.objects.filter(session=self, is_active=True,).order_by("mentor__user__last_name") + return MentorOrder.objects.filter( + session=self, + is_active=True, + ).order_by("mentor__user__last_name") def get_checked_in_mentor_orders(self): from .mentor_order import MentorOrder @@ -225,8 +311,14 @@ def get_mentor_capacity(self): class PartnerPasswordAccess(CommonInfo): from .user import CDCUser - user = models.ForeignKey(CDCUser, on_delete=models.CASCADE,) - session = models.ForeignKey(Session, on_delete=models.CASCADE,) + user = models.ForeignKey( + CDCUser, + on_delete=models.CASCADE, + ) + session = models.ForeignKey( + Session, + on_delete=models.CASCADE, + ) class Meta: db_table = "partner_password_access" diff --git a/coderdojochi/models/student.py b/coderdojochi/models/student.py index 3f3431de..80f730d6 100644 --- a/coderdojochi/models/student.py +++ b/coderdojochi/models/student.py @@ -8,16 +8,40 @@ class Student(CommonInfo): from .guardian import Guardian - guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE,) - first_name = models.CharField(max_length=255,) - last_name = models.CharField(max_length=255,) + guardian = models.ForeignKey( + Guardian, + on_delete=models.CASCADE, + ) + first_name = models.CharField( + max_length=255, + ) + last_name = models.CharField( + max_length=255, + ) birthday = models.DateTimeField() - gender = models.CharField(max_length=255,) - race_ethnicity = models.ManyToManyField(RaceEthnicity, blank=True,) - school_name = models.CharField(max_length=255, null=True,) - school_type = models.CharField(max_length=255, null=True,) - medical_conditions = models.TextField(blank=True, null=True,) - medications = models.TextField(blank=True, null=True,) + gender = models.CharField( + max_length=255, + ) + race_ethnicity = models.ManyToManyField( + RaceEthnicity, + blank=True, + ) + school_name = models.CharField( + max_length=255, + null=True, + ) + school_type = models.CharField( + max_length=255, + null=True, + ) + medical_conditions = models.TextField( + blank=True, + null=True, + ) + medications = models.TextField( + blank=True, + null=True, + ) photo_release = models.BooleanField( "Photo Consent", help_text=( @@ -31,7 +55,9 @@ class Student(CommonInfo): help_text=("I hereby give consent for the student signed up " "above to participate in We All Code."), default=False, ) - is_active = models.BooleanField(default=True,) + is_active = models.BooleanField( + default=True, + ) def __str__(self): return f"{self.first_name} {self.last_name}" @@ -41,7 +67,9 @@ def is_registered_for_session(self, session): try: Order.objects.get( - is_active=True, student=self, session=session, + is_active=True, + student=self, + session=session, ) is_registered = True except Exception: @@ -84,4 +112,3 @@ def is_within_gender_limitation(self, limitation): return False else: return True - diff --git a/coderdojochi/models/user.py b/coderdojochi/models/user.py index b7eaa297..5ffdac92 100644 --- a/coderdojochi/models/user.py +++ b/coderdojochi/models/user.py @@ -15,9 +15,17 @@ class CDCUser(AbstractUser): (GUARDIAN, "guardian"), ] - role = models.CharField(choices=ROLE_CHOICES, max_length=10, blank=True, null=True,) - - admin_notes = models.TextField(blank=True, null=True,) + role = models.CharField( + choices=ROLE_CHOICES, + max_length=10, + blank=True, + null=True, + ) + + admin_notes = models.TextField( + blank=True, + null=True, + ) @cached_property def name(self): diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index bda34d53..dbedb937 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -42,7 +42,10 @@ def home(request, template_name="home.html"): - upcoming_classes = Session.objects.filter(is_active=True, start_date__gte=timezone.now(),).order_by("start_date") + upcoming_classes = Session.objects.filter( + is_active=True, + start_date__gte=timezone.now(), + ).order_by("start_date") if not request.user.is_authenticated or not request.user.role == "mentor": upcoming_classes = upcoming_classes.filter(is_public=True) @@ -55,7 +58,12 @@ def home(request, template_name="home.html"): def volunteer(request, template_name="volunteer.html"): mentors = ( Mentor.objects.select_related("user") - .filter(is_active=True, is_public=True, background_check=True, avatar_approved=True,) + .filter( + is_active=True, + is_public=True, + background_check=True, + avatar_approved=True, + ) .annotate(session_count=Count("mentororder")) .order_by("-user__role", "-session_count") ) @@ -114,7 +122,9 @@ def mentor_reject_avatar(request, pk=None): email( subject="Your We All Code avatar...", template_name="class-announcement-mentor", - merge_global_data={"site_url": settings.SITE_URL,}, + merge_global_data={ + "site_url": settings.SITE_URL, + }, recipients=[mentor.user.email], ) @@ -183,7 +193,11 @@ def cdc_admin(request, template_name="admin.html"): .annotate( num_orders=Count("order"), num_attended=Count(Case(When(order__check_in__isnull=False, then=1))), - is_future=Case(When(start_date__gte=timezone.now(), then=1), default=0, output_field=IntegerField(),), + is_future=Case( + When(start_date__gte=timezone.now(), then=1), + default=0, + output_field=IntegerField(), + ), ) .order_by("-start_date") ) @@ -193,7 +207,11 @@ def cdc_admin(request, template_name="admin.html"): .annotate( num_orders=Count("meetingorder"), num_attended=Count(Case(When(meetingorder__check_in__isnull=False, then=1))), - is_future=Case(When(end_date__gte=timezone.now(), then=1), default=0, output_field=IntegerField(),), + is_future=Case( + When(end_date__gte=timezone.now(), then=1), + default=0, + output_field=IntegerField(), + ), ) .order_by("-start_date") ) @@ -516,7 +534,11 @@ def meeting_check_in(request, meeting_id, template_name="meeting-check-in.html") return render( request, template_name, - {"active_orders": active_orders, "inactive_orders": inactive_orders, "checked_in": checked_in,}, + { + "active_orders": active_orders, + "inactive_orders": inactive_orders, + "checked_in": checked_in, + }, ) @@ -552,7 +574,10 @@ def session_announce_mentors(request, pk): recipients = [] # send mentor announcements - mentors = Mentor.objects.filter(is_active=True, user__is_active=True,) + mentors = Mentor.objects.filter( + is_active=True, + user__is_active=True, + ) for mentor in mentors: recipients.append(mentor.user.email) @@ -613,7 +638,10 @@ def session_announce_guardians(request, pk): } recipients = [] - guardians = Guardian.objects.filter(is_active=True, user__is_active=True,) + guardians = Guardian.objects.filter( + is_active=True, + user__is_active=True, + ) for guardian in guardians: recipients.append(guardian.user.email) @@ -655,7 +683,10 @@ def check_system(request): halfday = timedelta(hours=12) # halfday = timedelta(seconds=15) - if Session.objects.filter(is_active=True, start_date__lte=timezone.now(),).count(): + if Session.objects.filter( + is_active=True, + start_date__lte=timezone.now(), + ).count(): runUpdate = False # uuid is posted from the computer using a bash script diff --git a/coderdojochi/settings.py b/coderdojochi/settings.py index c66cc902..f44daa49 100644 --- a/coderdojochi/settings.py +++ b/coderdojochi/settings.py @@ -29,7 +29,10 @@ # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = env("SECRET_KEY", default="!!!SET SECRET_KEY!!!",) +SECRET_KEY = env( + "SECRET_KEY", + default="!!!SET SECRET_KEY!!!", +) # SECURITY WARNING: don't run with debug turned on in production! DEBUG = env.bool("DEBUG", default=False) @@ -175,10 +178,18 @@ # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ - {"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",}, - {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",}, - {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",}, - {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",}, + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, ] diff --git a/coderdojochi/templatetags/coderdojochi_extras.py b/coderdojochi/templatetags/coderdojochi_extras.py index fbb3e8ff..34540c9f 100644 --- a/coderdojochi/templatetags/coderdojochi_extras.py +++ b/coderdojochi/templatetags/coderdojochi_extras.py @@ -25,7 +25,13 @@ def student_session_order_count(student, session): def student_register_link(context, student, session): orders = Order.objects.filter(student=student, session=session, is_active=True) - url = reverse("session-sign-up", kwargs={"pk": session.id, "student_id": student.id,}) + url = reverse( + "session-sign-up", + kwargs={ + "pk": session.id, + "student_id": student.id, + }, + ) button_tag = "a" button_modifier = "" diff --git a/coderdojochi/urls.py b/coderdojochi/urls.py index e573ea9c..924c4cb8 100644 --- a/coderdojochi/urls.py +++ b/coderdojochi/urls.py @@ -229,9 +229,21 @@ # This allows the error pages to be debugged during development, just visit # these url in browser to see how these error pages look like. urlpatterns += [ - path("400/", defaults.bad_request, kwargs={"exception": Exception("Bad Request!")},), - path("403/", defaults.permission_denied, kwargs={"exception": Exception("Permission Denied")},), - path("404/", defaults.page_not_found, kwargs={"exception": Exception("Page not Found")},), + path( + "400/", + defaults.bad_request, + kwargs={"exception": Exception("Bad Request!")}, + ), + path( + "403/", + defaults.permission_denied, + kwargs={"exception": Exception("Permission Denied")}, + ), + path( + "404/", + defaults.page_not_found, + kwargs={"exception": Exception("Page not Found")}, + ), path("500/", defaults.server_error), ] diff --git a/coderdojochi/views/guardian/sessions.py b/coderdojochi/views/guardian/sessions.py index 3874c304..d43bf57f 100644 --- a/coderdojochi/views/guardian/sessions.py +++ b/coderdojochi/views/guardian/sessions.py @@ -19,4 +19,3 @@ def get_context_data(self, **kwargs): # context["spots_remaining"] = object.capacity - object.get_active_student_count() return context - diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index b8f6938b..e768716b 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -203,7 +203,10 @@ def meeting_announce(request, pk): "meeting_calendar_url": f"{settings.SITE_URL}{meeting_obj.get_calendar_url()}", } - mentors = Mentor.objects.filter(is_active=True, user__is_active=True,) + mentors = Mentor.objects.filter( + is_active=True, + user__is_active=True, + ) recipients = [] for mentor in mentors: diff --git a/coderdojochi/views/mentor/sessions.py b/coderdojochi/views/mentor/sessions.py index 7fb6acec..c416f3f9 100644 --- a/coderdojochi/views/mentor/sessions.py +++ b/coderdojochi/views/mentor/sessions.py @@ -14,4 +14,3 @@ def get_context_data(self, **kwargs): mentor = get_object_or_404(Mentor, user=self.request.user) return context - diff --git a/coderdojochi/views/profile.py b/coderdojochi/views/profile.py index aea39b40..525c17ca 100644 --- a/coderdojochi/views/profile.py +++ b/coderdojochi/views/profile.py @@ -30,7 +30,10 @@ def get_context_data(self, **kwargs): mentor = get_object_or_404(Mentor, user=self.request.user) context["mentor"] = mentor - orders = MentorOrder.objects.select_related().filter(is_active=True, mentor=context["mentor"],) + orders = MentorOrder.objects.select_related().filter( + is_active=True, + mentor=context["mentor"], + ) # upcoming_sessions = orders.filter(is_active=True, session__start_date__gte=timezone.now()).order_by( # "session__start_date" diff --git a/coderdojochi/views/public/mentor.py b/coderdojochi/views/public/mentor.py index 1a7f606c..ec9aa82c 100644 --- a/coderdojochi/views/public/mentor.py +++ b/coderdojochi/views/public/mentor.py @@ -9,7 +9,11 @@ class MentorListView(ListView): def get_queryset(self): return ( Mentor.objects.filter( - user__is_active=True, is_active=True, is_public=True, background_check=True, avatar_approved=True, + user__is_active=True, + is_active=True, + is_public=True, + background_check=True, + avatar_approved=True, ) .select_related("user") .order_by("user__date_joined") @@ -21,5 +25,9 @@ class MentorDetailView(DetailView): def get_queryset(self): return Mentor.objects.filter( - user__is_active=True, is_active=True, is_public=True, background_check=True, avatar_approved=True, + user__is_active=True, + is_active=True, + is_public=True, + background_check=True, + avatar_approved=True, ).select_related("user") diff --git a/coderdojochi/views/public/sessions.py b/coderdojochi/views/public/sessions.py index db59d1cf..fc7d3d7a 100644 --- a/coderdojochi/views/public/sessions.py +++ b/coderdojochi/views/public/sessions.py @@ -12,4 +12,3 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) return context - diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index e9655dc9..cfe8951c 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -301,7 +301,12 @@ def post(self, request, *args, **kwargs): if mentor: order = get_object_or_404(MentorOrder, mentor=mentor, session=session_obj) elif student: - order = get_object_or_404(Order, student=student, session=session_obj, is_active=True,) + order = get_object_or_404( + Order, + student=student, + session=session_obj, + is_active=True, + ) order.is_active = False order.save() @@ -313,9 +318,16 @@ def post(self, request, *args, **kwargs): ip = request.META["HTTP_X_FORWARDED_FOR"] or request.META["REMOTE_ADDR"] if mentor: - order, created = MentorOrder.objects.get_or_create(mentor=mentor, session=session_obj,) + order, created = MentorOrder.objects.get_or_create( + mentor=mentor, + session=session_obj, + ) else: - order, created = Order.objects.get_or_create(guardian=guardian, student=student, session=session_obj,) + order, created = Order.objects.get_or_create( + guardian=guardian, + student=student, + session=session_obj, + ) order.ip = ip order.is_active = True @@ -425,7 +437,9 @@ def get_location(self, request, event_obj): guardian = Guardian.objects.get(user=self.request.user) students = guardian.get_students() students_signed_up = Order.objects.filter( - session=event_obj, is_active=True, student__in=students, + session=event_obj, + is_active=True, + student__in=students, ).exists() if students_signed_up: diff --git a/tasks.py b/tasks.py index f1c3d394..45770859 100644 --- a/tasks.py +++ b/tasks.py @@ -41,9 +41,15 @@ def test(ctx, app=""): @task def format(ctx): - # ctx.run('autopep8 -iaarj4 --exclude="**/migrations/*" --max-line-length="120" .') - ctx.run("black .") - ctx.run("autoflake --in-place --recursive --exclude '*/migrations/*' --remove-all-unused-imports .") + ctx.run("pip install -qU black isort") ctx.run("isort -m 3 --trailing-comma .") ctx.run("black .") - ctx.run("isort -m 3 --trailing-comma .") + # ctx.run("isort -m 3 --trailing-comma .") + # ctx.run("black .") + # ctx.run("black .") + # ctx.run('autopep8 -iaarj4 --exclude="**/migrations/*" --max-line-length="120" .') + # ctx.run("pip install -qU black autoflake isort") + # ctx.run("black --version") + # ctx.run( + # "autoflake --in-place --recursive --exclude '*/migrations/*' --exclude '*/__init__.py' --remove-all-unused-imports ." + # ) diff --git a/weallcode/forms.py b/weallcode/forms.py index 5a999a54..a156a391 100644 --- a/weallcode/forms.py +++ b/weallcode/forms.py @@ -17,14 +17,24 @@ class ContactForm(forms.Form): ("captcha", ""), ) - captcha = ReCaptchaField(widget=ReCaptchaV3,) + captcha = ReCaptchaField( + widget=ReCaptchaV3, + ) - name = forms.CharField(max_length=100, label="Full Name",) + name = forms.CharField( + max_length=100, + label="Full Name", + ) email = forms.EmailField( max_length=200, label="Email Address", - widget=forms.TextInput(attrs={"type": "email", "placeholder": "email@example.com",},), + widget=forms.TextInput( + attrs={ + "type": "email", + "placeholder": "email@example.com", + }, + ), ) interest = forms.ChoiceField( @@ -42,13 +52,24 @@ class ContactForm(forms.Form): max_length=20, label="Phone Number", widget=forms.TextInput( - attrs={"type": "tel", "placeholder": "+1 555 555-5555", "minlength": "10", "maxlength": "20",}, + attrs={ + "type": "tel", + "placeholder": "+1 555 555-5555", + "minlength": "10", + "maxlength": "20", + }, ), ) message = forms.CharField( label="Message", - widget=forms.Textarea(attrs={"placeholder": "Enter your message", "minlength": 25, "maxlength": 500,},), + widget=forms.Textarea( + attrs={ + "placeholder": "Enter your message", + "minlength": 25, + "maxlength": 500, + }, + ), ) def as_grid(self): @@ -70,5 +91,9 @@ def send_email(self): recipients=[settings.CONTACT_EMAIL], reply_to=[f"{data['name']}<{data['email']}>"], template_name="contact-email", - merge_global_data={"interest": data["interest"], "message": data["message"], "phone": data["phone"],}, + merge_global_data={ + "interest": data["interest"], + "message": data["message"], + "phone": data["phone"], + }, ) diff --git a/weallcode/models/associate_board_member.py b/weallcode/models/associate_board_member.py index b3daf6b4..70fbd311 100644 --- a/weallcode/models/associate_board_member.py +++ b/weallcode/models/associate_board_member.py @@ -8,7 +8,11 @@ class AssociateBoardMember(CommonInfo): - image = models.ImageField(upload_to="associate-board/", blank=True, null=True,) + image = models.ImageField( + upload_to="associate-board/", + blank=True, + null=True, + ) objects = CommonBoardMemberManager() diff --git a/weallcode/models/board_member.py b/weallcode/models/board_member.py index d722aa2f..c4761487 100644 --- a/weallcode/models/board_member.py +++ b/weallcode/models/board_member.py @@ -5,10 +5,13 @@ class BoardMember(CommonInfo): - image = models.ImageField(upload_to="board/", blank=True, null=True,) + image = models.ImageField( + upload_to="board/", + blank=True, + null=True, + ) objects = CommonBoardMemberManager() def __str__(self): return self.name - diff --git a/weallcode/models/common.py b/weallcode/models/common.py index 6bf0d2e9..a6599e18 100644 --- a/weallcode/models/common.py +++ b/weallcode/models/common.py @@ -22,15 +22,39 @@ class CommonInfo(models.Model): - name = models.CharField(max_length=255,) - role = models.CharField(choices=ROLE_CHOICES, max_length=255, default=DIRECTOR,) - image = models.ImageField(upload_to="staff/", blank=True, null=True,) - description = models.CharField(max_length=255, blank=True, null=True,) - linkedin = models.URLField(max_length=200, blank=True, null=True,) + name = models.CharField( + max_length=255, + ) + role = models.CharField( + choices=ROLE_CHOICES, + max_length=255, + default=DIRECTOR, + ) + image = models.ImageField( + upload_to="staff/", + blank=True, + null=True, + ) + description = models.CharField( + max_length=255, + blank=True, + null=True, + ) + linkedin = models.URLField( + max_length=200, + blank=True, + null=True, + ) join_date = models.DateField("Join Date", auto_now_add=True) - is_active = models.BooleanField(default=True,) - created_at = models.DateTimeField(auto_now_add=True,) - updated_at = models.DateTimeField(auto_now=True,) + is_active = models.BooleanField( + default=True, + ) + created_at = models.DateTimeField( + auto_now_add=True, + ) + updated_at = models.DateTimeField( + auto_now=True, + ) class Meta: abstract = True diff --git a/weallcode/models/staff.py b/weallcode/models/staff.py index b02a9129..9aedef43 100644 --- a/weallcode/models/staff.py +++ b/weallcode/models/staff.py @@ -10,12 +10,17 @@ def get_queryset(self): class StaffMember(CommonInfo): - role = models.CharField(max_length=255,) + role = models.CharField( + max_length=255, + ) - image = models.ImageField(upload_to="staff/", blank=True, null=True,) + image = models.ImageField( + upload_to="staff/", + blank=True, + null=True, + ) objects = StaffMemberManager() def __str__(self): return self.name - diff --git a/weallcode/views/associate_board.py b/weallcode/views/associate_board.py index 4b0032a5..2573777d 100644 --- a/weallcode/views/associate_board.py +++ b/weallcode/views/associate_board.py @@ -9,4 +9,3 @@ class AssociateBoardView(DefaultMetaTags, TemplateView): url = reverse_lazy("weallcode-associate-board") title = f"Join our Associate Board | We All Code" - diff --git a/weallcode/views/common.py b/weallcode/views/common.py index 2cc728d9..6ed02c4e 100644 --- a/weallcode/views/common.py +++ b/weallcode/views/common.py @@ -26,7 +26,13 @@ def page_not_found_view(*args, **kwargs): capture_message("Page not found!", level="error") return render( - request, "404.html", {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, status=404, + request, + "404.html", + { + "sentry_event_id": last_event_id(), + "SENTRY_DSN": settings.SENTRY_DSN, + }, + status=404, ) @@ -40,4 +46,3 @@ class DefaultMetaTags(MetadataMixin): twitter_card = "summary_large_image" twitter_creator = "@weallcode" twitter_site = "@weallcode" - diff --git a/weallcode/views/home.py b/weallcode/views/home.py index ffa83402..210d07c6 100644 --- a/weallcode/views/home.py +++ b/weallcode/views/home.py @@ -23,4 +23,3 @@ def get_context_data(self, **kwargs): context["next_session"] = sessions[0] return context - diff --git a/weallcode/views/join_us.py b/weallcode/views/join_us.py index 0785f526..b8f1331a 100644 --- a/weallcode/views/join_us.py +++ b/weallcode/views/join_us.py @@ -19,8 +19,8 @@ def form_valid(self, form): # It should return an HttpResponse. form.send_email() messages.success( - self.request, "Thank you for contacting us! We will respond as soon as possible.", + self.request, + "Thank you for contacting us! We will respond as soon as possible.", ) return super().form_valid(form) - diff --git a/weallcode/views/our_story.py b/weallcode/views/our_story.py index f0222f6f..ebc547ab 100644 --- a/weallcode/views/our_story.py +++ b/weallcode/views/our_story.py @@ -9,4 +9,3 @@ class OurStoryView(DefaultMetaTags, TemplateView): url = reverse_lazy("weallcode-our-story") title = f"Our Story | We All Code" - diff --git a/weallcode/views/programs.py b/weallcode/views/programs.py index 5437c41c..627bb066 100644 --- a/weallcode/views/programs.py +++ b/weallcode/views/programs.py @@ -17,9 +17,15 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # WEEKEND CLASSES - weekend_classes = Session.objects.filter( - is_active=True, start_date__gte=timezone.now(), course__course_type=Course.WEEKEND, - ).order_by("start_date").prefetch_related('course', 'location') + weekend_classes = ( + Session.objects.filter( + is_active=True, + start_date__gte=timezone.now(), + course__course_type=Course.WEEKEND, + ) + .order_by("start_date") + .prefetch_related("course", "location") + ) if not self.request.user.is_authenticated or not self.request.user.role == "mentor": weekend_classes = weekend_classes.filter(is_public=True) @@ -27,9 +33,15 @@ def get_context_data(self, **kwargs): context["weekend_classes"] = weekend_classes # SUMMER CAMP CLASSES - summer_camp_classes = Session.objects.filter( - is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, - ).order_by("start_date").prefetch_related('course', 'location') + summer_camp_classes = ( + Session.objects.filter( + is_active=True, + start_date__gte=timezone.now(), + course__course_type=Course.CAMP, + ) + .order_by("start_date") + .prefetch_related("course", "location") + ) if not self.request.user.is_authenticated or not self.request.user.role == "mentor": summer_camp_classes = summer_camp_classes.filter(is_public=True) @@ -37,4 +49,3 @@ def get_context_data(self, **kwargs): context["summer_camp_classes"] = summer_camp_classes return context - diff --git a/weallcode/views/programs_summer_camps.py b/weallcode/views/programs_summer_camps.py index 5002a618..11216b3d 100644 --- a/weallcode/views/programs_summer_camps.py +++ b/weallcode/views/programs_summer_camps.py @@ -15,7 +15,9 @@ def get_context_data(self, **kwargs): # SUMMER CAMP CLASSES summer_camp_classes = Session.objects.filter( - is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, + is_active=True, + start_date__gte=timezone.now(), + course__course_type=Course.CAMP, ).order_by("start_date") if not self.request.user.is_authenticated or not self.request.user.role == "mentor": diff --git a/weallcode/views/team.py b/weallcode/views/team.py index 595d06f9..ca488919 100644 --- a/weallcode/views/team.py +++ b/weallcode/views/team.py @@ -21,7 +21,9 @@ class TeamView(DefaultMetaTags, TemplateView): # Instructors def get_instructors(self, context, volunteers): - context["instructors"] = volunteers.filter(user__groups__name__in=["Instructor"],).order_by("user__first_name") + context["instructors"] = volunteers.filter( + user__groups__name__in=["Instructor"], + ).order_by("user__first_name") return context @@ -51,7 +53,10 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) volunteers = Mentor.objects.select_related("user").filter( - is_active=True, is_public=True, background_check=True, avatar_approved=True, + is_active=True, + is_public=True, + background_check=True, + avatar_approved=True, ) context["staff"] = StaffMember.objects.all() @@ -61,4 +66,3 @@ def get_context_data(self, **kwargs): context = self.get_volunteers(context, volunteers) return context - From 9442b24995814c527854446a9bd8f295e537e601 Mon Sep 17 00:00:00 2001 From: Robert Groves Date: Wed, 13 Jan 2021 13:27:13 -0600 Subject: [PATCH 09/57] Fix mismatched HTML tag. --- coderdojochi/templates/coderdojochi/mentor_list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderdojochi/templates/coderdojochi/mentor_list.html b/coderdojochi/templates/coderdojochi/mentor_list.html index 10c52bd5..54d37764 100644 --- a/coderdojochi/templates/coderdojochi/mentor_list.html +++ b/coderdojochi/templates/coderdojochi/mentor_list.html @@ -14,7 +14,7 @@

Meet Our Mentors Photo of {{ mentor.user.first_name }} {{ mentor.user.last_name }} -

{{ mentor.user.first_name }} {{ mentor.user.last_name }} +

{{ mentor.user.first_name }} {{ mentor.user.last_name }}

{% endfor %} From 36ee26e990b4ae259a8da677c053a208dfb843d6 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Wed, 13 Jan 2021 15:15:05 -0600 Subject: [PATCH 10/57] Add format on startup for debug state --- Pipfile | 2 - Pipfile.lock | 210 +++++++++++++++------------------------------------ tasks.py | 12 +-- 3 files changed, 62 insertions(+), 162 deletions(-) diff --git a/Pipfile b/Pipfile index 91350354..5796838a 100644 --- a/Pipfile +++ b/Pipfile @@ -37,8 +37,6 @@ sentry-sdk = "*" django-fullurl = "*" [dev-packages] -pylint = "*" -"autopep8" = "*" [requires] python_version = "3.8.6" diff --git a/Pipfile.lock b/Pipfile.lock index b0543902..58e61bd4 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "657c59f45970263cfd717b260ed8371493c6db60acd47734f33ee8b234492464" + "sha256": "f7cde9f56d6c08216ed9eeb1bcce3340383cbb933ee7ef6d3efd3161934db665" }, "pipfile-spec": 6, "requires": { @@ -34,18 +34,18 @@ }, "boto3": { "hashes": [ - "sha256:ba8de10d3ede338d51ae47e428b97dcc1d1b507741aa98697e63e879a147f4aa", - "sha256:e3f10ed6d9ca98415fdec15c85e50a89ec38d6229bce3fafd5e7965b16c4ebc5" + "sha256:15d3910eb1acfcdaa6aa8a64f3ed956ada1b5f3f9a8f2f127fd786036cb2afa5", + "sha256:d46f7c8c586c9c293f1a1eead56c18b82fcc3eec4dcebd4686aad4a675fd45b8" ], "index": "pypi", - "version": "==1.16.44" + "version": "==1.16.54" }, "botocore": { "hashes": [ - "sha256:4ff05bc089ba78a5996f06dcfddf8ca51583e30ce779ed95e9952e90c1907420", - "sha256:7725e08c95ae96c4dbd955cb4ae44a0c06d3e41f76a7feb0a941c27a44c63113" + "sha256:bba883f97c6657df941598150bf8a5a88bb976b7de53ec41ad3e518cfe8931d3", + "sha256:edfe213f3b01467605754ba1d46c6ac3b8a4be721fdf879d45c2132a99b4ac49" ], - "version": "==1.19.44" + "version": "==1.19.54" }, "certifi": { "hashes": [ @@ -147,11 +147,11 @@ }, "django": { "hashes": [ - "sha256:5c866205f15e7a7123f1eec6ab939d22d5bde1416635cab259684af66d8e48a2", - "sha256:edb10b5c45e7e9c0fb1dc00b76ec7449aca258a39ffd613dbd078c51d19c9f03" + "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7", + "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9" ], "index": "pypi", - "version": "==3.1.4" + "version": "==3.1.5" }, "django-allauth": { "hashes": [ @@ -246,11 +246,11 @@ }, "django-import-export": { "hashes": [ - "sha256:401d76eca0a5c6cf43bffed16c06e509b9044ce8f6bcff264b776e3952830f1a", - "sha256:7610f6efff797d65f56c03ba34444507c0b0ccdffe9346c168b9894fc349c55e" + "sha256:c39c003bfc803fb63ba7742562f1667603a4a8d7426261845d75ce8582d40f48", + "sha256:cf6f3dabdd4f32dcb26e25c7ddcba7aee3168b55d380b0da79f0349afa17c011" ], "index": "pypi", - "version": "==2.4.0" + "version": "==2.5.0" }, "django-loginas": { "hashes": [ @@ -315,11 +315,11 @@ }, "faker": { "hashes": [ - "sha256:00ce4342c221b1931b2f35d46f5027d35bc62a4ca3a34628b2c5b514b4ca958a", - "sha256:5b17c95cfb013a22b062b8df18286f08ce4ea880f9948ec74295e5a42dbb2e44" + "sha256:3d8ba3e4a3700962a1bf5599a43797b286d1ac52b3dd90c1e8a2625f73c46bd6", + "sha256:eb14d0c6b8f7c9902bb07dedc2ad405237edbf808d4ead9f17b9567e586cabee" ], "markers": "python_version >= '3.6'", - "version": "==5.0.2" + "version": "==5.6.0" }, "gunicorn": { "hashes": [ @@ -347,12 +347,12 @@ }, "invoke": { "hashes": [ - "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132", - "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134", - "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d" + "sha256:7e44d98a7dc00c91c79bac9e3007276965d2c96884b3c22077a9f04042bd6d90", + "sha256:da7c2d0be71be83ffd6337e078ef9643f41240024d6b2659e7b46e0b251e339f", + "sha256:f0c560075b5fb29ba14dad44a7185514e94970d1b9d57dcd3723bec5fed92650" ], "index": "pypi", - "version": "==1.4.1" + "version": "==1.5.0" }, "jdcal": { "hashes": [ @@ -422,37 +422,41 @@ }, "pillow": { "hashes": [ - "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a", - "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae", - "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce", - "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e", - "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140", - "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb", - "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021", - "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6", - "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302", - "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c", - "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271", - "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09", - "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3", - "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015", - "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3", - "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544", - "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8", - "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792", - "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0", - "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3", - "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8", - "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11", - "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7", - "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11", - "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e", - "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039", - "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5", - "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72" - ], - "index": "pypi", - "version": "==8.0.1" + "sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6", + "sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded", + "sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865", + "sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174", + "sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032", + "sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a", + "sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e", + "sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378", + "sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17", + "sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c", + "sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913", + "sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7", + "sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0", + "sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820", + "sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba", + "sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2", + "sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b", + "sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9", + "sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234", + "sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d", + "sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5", + "sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206", + "sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9", + "sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8", + "sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59", + "sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d", + "sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7", + "sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a", + "sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0", + "sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b", + "sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d", + "sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae" + ], + "index": "pypi", + "version": "==8.1.0" }, "progressbar2": { "hashes": [ @@ -607,10 +611,10 @@ }, "s3transfer": { "hashes": [ - "sha256:2482b4259524933a022d59da830f51bd746db62f047d6eb213f2f8855dcb8a13", - "sha256:921a37e2aefc64145e7b73d50c71bb4f26f46e4c9f414dc648c6245ff92cf7db" + "sha256:1e28620e5b444652ed752cf87c7e0cb15b0e578972568c6609f0f18212f259ed", + "sha256:7fdddb4f22275cf1d32129e21f056337fd2a80b6ccef1664528145b72c49e6d2" ], - "version": "==0.3.3" + "version": "==0.3.4" }, "sentry-sdk": { "hashes": [ @@ -688,101 +692,5 @@ "version": "==1.3.0" } }, - "develop": { - "astroid": { - "hashes": [ - "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703", - "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386" - ], - "markers": "python_version >= '3.5'", - "version": "==2.4.2" - }, - "autopep8": { - "hashes": [ - "sha256:d21d3901cb0da6ebd1e83fc9b0dfbde8b46afc2ede4fe32fbda0c7c6118ca094" - ], - "index": "pypi", - "version": "==1.5.4" - }, - "isort": { - "hashes": [ - "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7", - "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58" - ], - "markers": "python_version >= '3.6' and python_version < '4.0'", - "version": "==5.6.4" - }, - "lazy-object-proxy": { - "hashes": [ - "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", - "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", - "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", - "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", - "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", - "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", - "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", - "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", - "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", - "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", - "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", - "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", - "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", - "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", - "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", - "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", - "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", - "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", - "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", - "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", - "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.4.3" - }, - "mccabe": { - "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" - ], - "version": "==0.6.1" - }, - "pycodestyle": { - "hashes": [ - "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", - "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.6.0" - }, - "pylint": { - "hashes": [ - "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210", - "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f" - ], - "index": "pypi", - "version": "==2.6.0" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.15.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, - "wrapt": { - "hashes": [ - "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" - ], - "version": "==1.12.1" - } - } + "develop": {} } diff --git a/tasks.py b/tasks.py index 45770859..f7dddbfc 100644 --- a/tasks.py +++ b/tasks.py @@ -6,6 +6,9 @@ @task def release(ctx): + if env.bool("DEBUG", default=False): + format(ctx) + collect_static(ctx) migrate(ctx) load_fixtures(ctx) @@ -44,12 +47,3 @@ def format(ctx): ctx.run("pip install -qU black isort") ctx.run("isort -m 3 --trailing-comma .") ctx.run("black .") - # ctx.run("isort -m 3 --trailing-comma .") - # ctx.run("black .") - # ctx.run("black .") - # ctx.run('autopep8 -iaarj4 --exclude="**/migrations/*" --max-line-length="120" .') - # ctx.run("pip install -qU black autoflake isort") - # ctx.run("black --version") - # ctx.run( - # "autoflake --in-place --recursive --exclude '*/migrations/*' --exclude '*/__init__.py' --remove-all-unused-imports ." - # ) From 94eeb6473887957ad14f48f891fd8fea8c9e9017 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Wed, 13 Jan 2021 15:21:57 -0600 Subject: [PATCH 11/57] Fixed form content link --- coderdojochi/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderdojochi/forms.py b/coderdojochi/forms.py index 425ea19d..5fa07b39 100644 --- a/coderdojochi/forms.py +++ b/coderdojochi/forms.py @@ -314,7 +314,7 @@ class StudentForm(CDCModelForm): ), label=( "I hereby give consent for the student signed up above to participate in We All Code as per the " - f"terms." + f"terms." ), ) From c5be5ecbb1a97dece627bdd70ad481776d1f31af Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Wed, 13 Jan 2021 15:25:48 -0600 Subject: [PATCH 12/57] Delete old files --- coderdojochi/admin.py | 11 +- coderdojochi/old_views.py | 10 -- weallcode/views.py | 274 -------------------------------------- 3 files changed, 5 insertions(+), 290 deletions(-) delete mode 100644 weallcode/views.py diff --git a/coderdojochi/admin.py b/coderdojochi/admin.py index 2c95f65d..da3c3822 100644 --- a/coderdojochi/admin.py +++ b/coderdojochi/admin.py @@ -514,7 +514,6 @@ class SessionAdmin(ImportExportMixin, ImportExportActionModelAdmin): list_display = [ "_course", "start_date", - # 'end_date', "location", "capacity", "student_count_link", @@ -547,11 +546,11 @@ class SessionAdmin(ImportExportMixin, ImportExportActionModelAdmin): "waitlist_students", ] - autocomplete_fields = [ - # 'course', - # 'location', - # 'instructor', - ] + # autocomplete_fields = [ + # 'course', + # 'location', + # 'instructor', + # ] search_fields = ["start_date"] diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index dbedb937..76853718 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -381,16 +381,6 @@ def session_check_in(request, pk, template_name="session-check-in.html"): age_count = sorted(list(dict(list(Counter(ages).items())).items()), key=operator.itemgetter(0)) - # age_count = sorted( - # dict( - # list( - # Counter(ages).items() - # ) - # ).items(), - # key=operator.itemgetter(1), - # reverse=True - # ) - # Average Age if orders and ages: average_age = int(round(sum(ages) / float(len(ages)))) diff --git a/weallcode/views.py b/weallcode/views.py deleted file mode 100644 index eabd6c83..00000000 --- a/weallcode/views.py +++ /dev/null @@ -1,274 +0,0 @@ -# from collections import defaultdict -# from datetime import datetime -# from itertools import chain - -# from django.conf import settings -# from django.contrib import messages, sitemaps -# from django.contrib.auth import get_user_model -# from django.db.models import Count -# from django.urls import reverse, reverse_lazy -# from django.utils import timezone -# from django.views.generic import FormView, TemplateView - -# from meta.views import MetadataMixin -# from sentry_sdk import capture_message - -# from coderdojochi.models import Course, Mentor, Session - -# from .forms import ContactForm -# from .models import AssociateBoardMember, BoardMember, StaffMember - -# User = get_user_model() - - -# def page_not_found_view(*args, **kwargs): -# print("page_not_found_view") -# capture_message("Page not found!", level="error") - -# return render( -# request, "404.html", {"sentry_event_id": last_event_id(), "SENTRY_DSN": settings.SENTRY_DSN,}, status=404, -# ) - - -# class DefaultMetaTags(MetadataMixin): -# title = None -# description = ( -# "We All Code is volunteer run nonprofit organization that teaches web, game, and app development to " -# "youth ages 7 to 17 free of charge." -# ) -# image = "weallcode/images/photos/real-coding-skills.jpg" -# twitter_card = "summary_large_image" -# twitter_creator = "@weallcode" -# twitter_site = "@weallcode" - - -# class HomeView(DefaultMetaTags, TemplateView): -# template_name = "weallcode/home.html" -# url = reverse_lazy("weallcode-home") - -# def get_context_data(self, **kwargs): -# context = super().get_context_data(**kwargs) - -# sessions = Session.objects.filter(is_active=True, start_date__gte=timezone.now()).order_by("start_date") - -# if not self.request.user.is_authenticated or not self.request.user.role == "mentor": -# sessions = sessions.filter(is_public=True) - -# if len(sessions) > 0: -# context["next_session"] = sessions[0] - -# return context - - -# class OurStoryView(DefaultMetaTags, TemplateView): -# template_name = "weallcode/our_story.html" -# url = reverse_lazy("weallcode-our-story") - -# title = f"Our Story | We All Code" - - -# class ProgramsView(DefaultMetaTags, TemplateView): -# template_name = "weallcode/programs.html" -# url = reverse_lazy("weallcode-programs") - -# title = f"Programs | We All Code" - -# def get_context_data(self, **kwargs): -# context = super().get_context_data(**kwargs) - -# # WEEKEND CLASSES -# weekend_classes = Session.objects.filter( -# is_active=True, start_date__gte=timezone.now(), course__course_type=Course.WEEKEND, -# ).order_by("start_date") - -# if not self.request.user.is_authenticated or not self.request.user.role == "mentor": -# weekend_classes = weekend_classes.filter(is_public=True) - -# context["weekend_classes"] = weekend_classes - -# # SUMMER CAMP CLASSES -# summer_camp_classes = Session.objects.filter( -# is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, -# ).order_by("start_date") - -# if not self.request.user.is_authenticated or not self.request.user.role == "mentor": -# summer_camp_classes = summer_camp_classes.filter(is_public=True) - -# context["summer_camp_classes"] = summer_camp_classes - -# return context - - -# class ProgramsSummerCampsView(DefaultMetaTags, TemplateView): -# template_name = "weallcode/programs-summer-camps.html" -# url = reverse_lazy("weallcode-programs-summer-camps") - -# title = f"Summer Camps | We All Code" - -# def get_context_data(self, **kwargs): -# context = super().get_context_data(**kwargs) - -# # SUMMER CAMP CLASSES -# summer_camp_classes = Session.objects.filter( -# is_active=True, start_date__gte=timezone.now(), course__course_type=Course.CAMP, -# ).order_by("start_date") - -# if not self.request.user.is_authenticated or not self.request.user.role == "mentor": -# summer_camp_classes = summer_camp_classes.filter(is_public=True) - -# context["summer_camp_classes"] = summer_camp_classes - -# return context - - -# class TeamView(DefaultMetaTags, TemplateView): -# template_name = "weallcode/team.html" -# url = reverse_lazy("weallcode-team") - -# title = f"Team | We All Code" - -# # Staff -# def get_staff(self, context): -# context["staff"] = StaffMember.objects.filter(is_active=True,) - -# return context - -# # Board Members -# def get_board(self, context): -# board = BoardMember.objects.filter(is_active=True,) - -# # NOTE: We're splitting and reordering them manually since the website requires a specific order. -# roles = defaultdict(list) - -# for person in board: -# roles[person.role].append(person) - -# # The correct order -# context["board"] = list( -# chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) -# ) - -# return context - -# # Associate Board Members -# def get_associate_board(self, context): -# associate_board = AssociateBoardMember.objects.filter(is_active=True,).order_by("name") - -# # NOTE: We're splitting and reordering them manually since the website requires a specific order. -# roles = defaultdict(list) - -# for person in associate_board: -# roles[person.role].append(person) - -# # The correct order -# context["associate_board"] = list( -# chain(roles["Chair"], roles["Vice Chair"], roles["Treasurer"], roles["Secretary"], roles["Director"],) -# ) - -# return context - -# # Instructors -# def get_instructors(self, context, volunteers): -# context["instructors"] = volunteers.filter(user__groups__name__in=["Instructor"],).order_by("user__first_name") - -# return context - -# # Volunteers -# def get_volunteers(self, context, volunteers): -# all_volunteers = volunteers.annotate(session_count=Count("mentororder")).order_by( -# "-user__role", "-session_count" -# ) - -# mentors = [] -# volunteers = [] - -# for volunteer in all_volunteers: -# if volunteer.session_count >= 10: -# mentors += [volunteer] -# else: -# volunteers += [volunteer] - -# context["top_mentors"] = mentors -# # context['other_mentors'] = mentors[8:] -# context["top_volunteers"] = volunteers[0:16] -# context["other_volunteers"] = volunteers[16:] - -# return context - -# def get_context_data(self, **kwargs): -# context = super().get_context_data(**kwargs) - -# volunteers = Mentor.objects.select_related("user").filter( -# is_active=True, is_public=True, background_check=True, avatar_approved=True, -# ) - -# context = self.get_staff(context) -# context = self.get_board(context) -# context = self.get_associate_board(context) -# context = self.get_instructors(context, volunteers) -# context = self.get_volunteers(context, volunteers) - -# return context - - -# class JoinUsView(DefaultMetaTags, FormView): -# template_name = "weallcode/join_us.html" -# form_class = ContactForm -# url = reverse_lazy("weallcode-join-us") -# success_url = reverse_lazy("weallcode-join-us") - -# title = f"Join Us | We All Code" - -# def form_valid(self, form): -# # This method is called when valid form data has been POSTed. -# # It should return an HttpResponse. -# form.send_email() -# messages.success( -# self.request, "Thank you for contacting us! We will respond as soon as possible.", -# ) - -# return super().form_valid(form) - - -# class AssociateBoardView(DefaultMetaTags, TemplateView): -# template_name = "weallcode/associate_board.html" -# url = reverse_lazy("weallcode-associate-board") - -# title = f"Join our Associate Board | We All Code" - - -# class PrivacyView(DefaultMetaTags, TemplateView): -# template_name = "weallcode/privacy.html" -# url = reverse_lazy("weallcode-privacy") - -# title = f"Privacy & Terms | We All Code" - - -# class CreditsView(DefaultMetaTags, TemplateView): -# template_name = "weallcode/credits.html" -# url = reverse_lazy("weallcode-credits") - -# title = f"Credits & Attributions | We All Code" - - -# class StaticSitemapView(sitemaps.Sitemap): -# priority = 0.5 -# changefreq = "daily" - -# def items(self): -# return [ -# "weallcode-home", -# "weallcode-our-story", -# "weallcode-programs", -# "weallcode-programs-summer-camps", -# "weallcode-team", -# "weallcode-join-us", -# "weallcode-privacy", -# "weallcode-credits", -# ] - -# def location(self, item): -# return reverse(item) - -# def lastmod(self, item): -# return datetime.now() From b2a3c06784bf715b024b60a6bd78d5c1c58042fb Mon Sep 17 00:00:00 2001 From: Robert Groves Date: Wed, 13 Jan 2021 18:26:45 -0600 Subject: [PATCH 13/57] Fixed form content link --- coderdojochi/forms.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/coderdojochi/forms.py b/coderdojochi/forms.py index 5fa07b39..50cc5b5c 100644 --- a/coderdojochi/forms.py +++ b/coderdojochi/forms.py @@ -4,9 +4,11 @@ from django.contrib.auth import get_user_model from django.core.files.images import get_image_dimensions from django.forms import FileField, Form, ModelForm, ValidationError +from django.urls import reverse_lazy from django.utils import dateformat, timezone from django.utils.html import format_html from django.utils.safestring import mark_safe +from django.utils.text import format_lazy import html5.forms.widgets as html5_widgets from dateutil.relativedelta import relativedelta @@ -312,9 +314,10 @@ class StudentForm(CDCModelForm): "required": "required", }, ), - label=( + label=format_lazy( "I hereby give consent for the student signed up above to participate in We All Code as per the " - f"terms." + 'terms.', + reverse_lazy("weallcode-privacy"), ), ) From 8984b2bd8a8e49792b068546fa92c2aa937d3e07 Mon Sep 17 00:00:00 2001 From: Robert Groves Date: Wed, 13 Jan 2021 20:21:49 -0600 Subject: [PATCH 14/57] Fix unformatted string. --- coderdojochi/views/sessions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index cfe8951c..128cfc9e 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -257,7 +257,7 @@ def check_access(self, request, *args, **kwargs): access_dict = { "message": ( "You cannot sign up for a class until you " - 'fill out the background search form.' + f'fill out the background search form.' ), "redirect": request.META.get("HTTP_REFERER", "/dojo"), } From da85b3160076f78d7b9a8174eaa70f1bc2ed8c9c Mon Sep 17 00:00:00 2001 From: Robert Groves Date: Wed, 13 Jan 2021 22:43:41 -0600 Subject: [PATCH 15/57] Add missing context values required by template. --- coderdojochi/views/mentor/sessions.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/coderdojochi/views/mentor/sessions.py b/coderdojochi/views/mentor/sessions.py index c416f3f9..ced081a9 100644 --- a/coderdojochi/views/mentor/sessions.py +++ b/coderdojochi/views/mentor/sessions.py @@ -1,7 +1,7 @@ from django.shortcuts import get_object_or_404 from django.views.generic import DetailView -from ...models import Mentor, Session +from ...models import Mentor, MentorOrder, Session class SessionDetailView(DetailView): @@ -9,8 +9,18 @@ class SessionDetailView(DetailView): template_name = "mentor/session-detail.html" def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - + session = self.object mentor = get_object_or_404(Mentor, user=self.request.user) + session_orders = MentorOrder.objects.filter( + session=session, + mentor=mentor, + is_active=True, + ) + + context = super().get_context_data(**kwargs) + context["mentor_signed_up"] = session_orders.exists() + context["spots_remaining"] = session.get_mentor_capacity() - session_orders.count() + context["account"] = mentor + return context From 11de7820a3a073542e336967b5da21163a3e0dfe Mon Sep 17 00:00:00 2001 From: Robert Groves Date: Thu, 14 Jan 2021 00:15:08 -0600 Subject: [PATCH 16/57] Fix attempt to filter object instance. This was throwing an "AttributeError: Manager isn't accessible" error. --- coderdojochi/models/mentor_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderdojochi/models/mentor_order.py b/coderdojochi/models/mentor_order.py index 076d83ae..dd9b0b8f 100644 --- a/coderdojochi/models/mentor_order.py +++ b/coderdojochi/models/mentor_order.py @@ -56,7 +56,7 @@ def is_checked_in(self): is_checked_in.boolean = True def save(self, *args, **kwargs): - num_orders = self.objects.filter(mentor__id=self.mentor.id).count() + num_orders = MentorOrder.objects.filter(mentor__id=self.mentor.id).count() if self.pk is None and num_orders == 0: NewMentorOrderNotification(self).send() From a3aea519681040a235e2612895a3a52815245747 Mon Sep 17 00:00:00 2001 From: Robert Groves Date: Thu, 14 Jan 2021 09:43:50 -0600 Subject: [PATCH 17/57] Fix to use past sessions for past sessions list. This was incorrectly using upcoming session data in the past sessions list within the view. --- accounts/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/views.py b/accounts/views.py index 0d090aa3..6a00d1b5 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -114,7 +114,7 @@ def get_context_data_for_mentor(self): "mentor": mentor, "orders": orders, "upcoming_sessions": upcoming_sessions, - "past_sessions": upcoming_sessions, + "past_sessions": past_sessions, "meeting_orders": meeting_orders, "upcoming_meetings": upcoming_meetings, "account_complete": account_complete, From 2c2768bd38deab9e71a559ec6e635cede97fa453 Mon Sep 17 00:00:00 2001 From: Robert Groves Date: Thu, 14 Jan 2021 10:44:18 -0600 Subject: [PATCH 18/57] Add missing context values required by template. --- coderdojochi/views/guardian/sessions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderdojochi/views/guardian/sessions.py b/coderdojochi/views/guardian/sessions.py index d43bf57f..6b414e24 100644 --- a/coderdojochi/views/guardian/sessions.py +++ b/coderdojochi/views/guardian/sessions.py @@ -16,6 +16,6 @@ def get_context_data(self, **kwargs): guardian = get_object_or_404(Guardian, user=self.request.user) context["students"] = guardian.get_students() - # context["spots_remaining"] = object.capacity - object.get_active_student_count() + context["spots_remaining"] = self.object.capacity - self.object.get_active_student_count() return context From c098c9e5678dfebbe964aa39c5a986bfeb532679 Mon Sep 17 00:00:00 2001 From: Robert Groves Date: Thu, 14 Jan 2021 11:05:25 -0600 Subject: [PATCH 19/57] Fix bad class at capacity check. --- weallcode/templates/weallcode/snippets/class.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weallcode/templates/weallcode/snippets/class.html b/weallcode/templates/weallcode/snippets/class.html index adf73eb6..1c143159 100644 --- a/weallcode/templates/weallcode/snippets/class.html +++ b/weallcode/templates/weallcode/snippets/class.html @@ -47,7 +47,7 @@
{{ session.location.name }}
Volunteer {% endif %} {% else %} - {% if session.get_current_students.count < session.capacity %} + {% if session.get_active_student_count < session.capacity %} Learn more {% else %} Join Waitlist From 412fa6cc3a9f12c89958b6f093956c648ddd69f0 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Thu, 14 Jan 2021 13:47:41 -0600 Subject: [PATCH 20/57] avatar and model property helpers --- accounts/templates/account/mentor/home.html | 6 +- accounts/views.py | 4 +- coderdojochi/admin.py | 4 +- coderdojochi/cron.py | 28 +++---- coderdojochi/models/guardian.py | 6 +- coderdojochi/models/meeting.py | 2 +- coderdojochi/models/mentor.py | 84 ++++++++++++++++--- coderdojochi/models/mentor_order.py | 2 +- coderdojochi/notifications.py | 8 +- coderdojochi/old_views.py | 32 +++---- coderdojochi/signals_handlers.py | 6 +- .../templates/coderdojochi/mentor_detail.html | 8 +- .../templates/coderdojochi/mentor_list.html | 4 +- .../templates/dashboard/meeting-check-in.html | 4 +- .../dashboard/session-check-in-mentors.html | 10 +-- .../templates/dashboard/session-check-in.html | 12 +-- .../templates/dashboard/session-stats.html | 2 +- ...ession-detail.html => session_detail.html} | 8 +- coderdojochi/templates/meeting-detail.html | 4 +- ...ession-detail.html => session_detail.html} | 8 +- ...ession-detail.html => session_detail.html} | 8 +- coderdojochi/templates/volunteer.html | 8 +- coderdojochi/views/guardian/sessions.py | 7 +- coderdojochi/views/meetings.py | 8 +- coderdojochi/views/mentor/sessions.py | 2 +- coderdojochi/views/profile.py | 4 +- coderdojochi/views/public/sessions.py | 2 +- coderdojochi/views/sessions.py | 2 +- coderdojochi/views/welcome.py | 4 +- .../weallcode/snippets/team_member.html | 2 - 30 files changed, 173 insertions(+), 116 deletions(-) rename coderdojochi/templates/guardian/{session-detail.html => session_detail.html} (94%) rename coderdojochi/templates/mentor/{session-detail.html => session_detail.html} (96%) rename coderdojochi/templates/public/{session-detail.html => session_detail.html} (93%) diff --git a/accounts/templates/account/mentor/home.html b/accounts/templates/account/mentor/home.html index f0ffc1af..11c9ce98 100644 --- a/accounts/templates/account/mentor/home.html +++ b/accounts/templates/account/mentor/home.html @@ -9,12 +9,12 @@

- Hello {% if mentor.user.first_name %}{{ mentor.user.first_name }}{% else %}there friend{% endif %}! + Hello {% if mentor.first_name %}{{ mentor.first_name }}{% else %}there friend{% endif %}!

We're super excited to have you join our ranks. Before you come to your first mentor session, we need to take care of a few things.

    - {% if not mentor.user.first_name or not mentor.user.last_name %} + {% if not mentor.first_name or not mentor.last_name %}
  1. Enter full name.
  2. {% endif %} @@ -50,7 +50,7 @@

    Your Profile

    {% if mentor.avatar and not mentor.avatar_approved %} Pending Approval {% endif %} - +
    diff --git a/accounts/views.py b/accounts/views.py index 6a00d1b5..138da6d7 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -102,8 +102,8 @@ def get_context_data_for_mentor(self): account_complete = False if ( - mentor.user.first_name - and mentor.user.last_name + mentor.first_name + and mentor.last_name and mentor.avatar and mentor.background_check and past_sessions.count() > 0 diff --git a/coderdojochi/admin.py b/coderdojochi/admin.py index da3c3822..ea0cccae 100644 --- a/coderdojochi/admin.py +++ b/coderdojochi/admin.py @@ -454,8 +454,8 @@ def guardian_link(self, obj): return format_html( '{first} {last}', url=reverse("admin:coderdojochi_guardian_change", args=(obj.guardian.id,)), - first=obj.guardian.user.first_name, - last=obj.guardian.user.last_name, + first=obj.guardian.first_name, + last=obj.guardian.last_name, ) guardian_link.short_description = "Guardian" diff --git a/coderdojochi/cron.py b/coderdojochi/cron.py index 14601931..cc3bed9d 100644 --- a/coderdojochi/cron.py +++ b/coderdojochi/cron.py @@ -47,10 +47,10 @@ def do(self): recipients = [] for order in orders_within_a_week: - recipients.append(order.guardian.user.email) - merge_data[order.guardian.user.email] = { - "first_name": order.guardian.user.first_name, - "last_name": order.guardian.user.last_name, + recipients.append(order.guardian.email) + merge_data[order.guardian.email] = { + "first_name": order.guardian.first_name, + "last_name": order.guardian.last_name, "student_first_name": order.student.first_name, "student_last_name": order.student.last_name, "class_code": order.session.course.code, @@ -93,10 +93,10 @@ def do(self): recipients = [] for order in orders_within_a_day: - recipients.append(order.guardian.user.email) - merge_data[order.guardian.user.email] = { - "first_name": order.guardian.user.first_name, - "last_name": order.guardian.user.last_name, + recipients.append(order.guardian.email) + merge_data[order.guardian.email] = { + "first_name": order.guardian.first_name, + "last_name": order.guardian.last_name, "student_first_name": order.student.first_name, "student_last_name": order.student.last_name, "class_code": order.session.course.code, @@ -142,10 +142,10 @@ def do(self): recipients = [] for order in orders: - recipients.append(order.mentor.user.email) - merge_data[order.mentor.user.email] = { - "first_name": order.mentor.user.first_name, - "last_name": order.mentor.user.last_name, + recipients.append(order.mentor.email) + merge_data[order.mentor.email] = { + "first_name": order.mentor.first_name, + "last_name": order.mentor.last_name, "class_code": order.session.course.code, "class_title": order.session.course.title, "class_description": order.session.course.description, @@ -186,8 +186,8 @@ def do(self): orders = MentorOrder.objects.filter(session=session) for order in orders: - recipients.append(order.mentor.user.email) - merge_data[order.mentor.user.email] = { + recipients.append(order.mentor.email) + merge_data[order.mentor.email] = { "first_name": order.mentor.user.first_name, "last_name": order.mentor.user.last_name, "class_code": order.session.course.code, diff --git a/coderdojochi/models/guardian.py b/coderdojochi/models/guardian.py index 5db3c2eb..2dacb22f 100644 --- a/coderdojochi/models/guardian.py +++ b/coderdojochi/models/guardian.py @@ -37,7 +37,7 @@ class Guardian(CommonInfo): ) def __str__(self): - return f"{self.user.first_name} {self.user.last_name}" + return self.full_name @property def first_name(self): @@ -47,6 +47,10 @@ def first_name(self): def last_name(self): return self.user.last_name + @property + def full_name(self): + return f"{self.user.first_name} {self.user.last_name}" + @property def email(self): return self.user.email diff --git a/coderdojochi/models/meeting.py b/coderdojochi/models/meeting.py index 9fce32ce..01588efe 100644 --- a/coderdojochi/models/meeting.py +++ b/coderdojochi/models/meeting.py @@ -175,7 +175,7 @@ class MeetingOrder(CommonInfo): ) def __str__(self): - return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.meeting.meeting_type.title}" + return f"{self.mentor.full_name} | {self.meeting.meeting_type.title}" def is_checked_in(self): return self.check_in is not None diff --git a/coderdojochi/models/mentor.py b/coderdojochi/models/mentor.py index 99ff29bd..9c9131a3 100644 --- a/coderdojochi/models/mentor.py +++ b/coderdojochi/models/mentor.py @@ -44,7 +44,7 @@ class Mentor(CommonInfo): "width": 500, "height": 500, "crop": True, - } + }, }, ) avatar_approved = models.BooleanField( @@ -80,11 +80,24 @@ class Mentor(CommonInfo): ) def __str__(self): - return f"{self.user.first_name} {self.user.last_name}" + return self.full_name + + @property + def first_name(self): + return self.user.first_name + @property + def last_name(self): + return self.user.last_name + + @property def full_name(self): return f"{self.user.first_name} {self.user.last_name}" + @property + def email(self): + return self.user.email + def save(self, *args, **kwargs): if self.pk is None: NewMentorNotification(self).send() @@ -99,18 +112,67 @@ def save(self, *args, **kwargs): super(Mentor, self).save(*args, **kwargs) def get_approve_avatar_url(self): - return reverse("mentor-approve-avatar", args=[str(self.id)]) + return reverse( + "mentor-approve-avatar", + args=[ + str(self.id), + ], + ) def get_reject_avatar_url(self): - return reverse("mentor-reject-avatar", args=[str(self.id)]) + return reverse( + "mentor-reject-avatar", + args=[ + str(self.id), + ], + ) def get_absolute_url(self): - return reverse("mentor-detail", args=[str(self.id)]) + return reverse( + "mentor-detail", + args=[ + str(self.id), + ], + ) + + def get_avatar(self): + if ( + self.avatar + and self.avatar.storage.exists(self.avatar.name) + and self.avatar.storage.exists(self.avatar.thumbnail.name) + ): + return self.avatar + + # Gravatar + import hashlib + from urllib.parse import urlencode + + # https://en.gravatar.com/site/implement/images/ + + email = self.email.encode("utf-8").lower() + email_encoded = hashlib.md5(email).hexdigest() + + thumbnail_params = urlencode( + { + "d": "mp", + "r": "g", + "s": str(320), + } + ) + full_params = urlencode( + { + "d": "mp", + "r": "g", + "s": str(500), + } + ) + slug_url = f"https://www.gravatar.com/avatar/{email_encoded}" - @property - def first_name(self): - return self.user.first_name + avatar = { + "url": f"{slug_url}?{full_params}", + "thumbnail": { + "url": f"{slug_url}?{thumbnail_params}", + }, + } - @property - def last_name(self): - return self.user.last_name + return avatar diff --git a/coderdojochi/models/mentor_order.py b/coderdojochi/models/mentor_order.py index dd9b0b8f..923c4d37 100644 --- a/coderdojochi/models/mentor_order.py +++ b/coderdojochi/models/mentor_order.py @@ -48,7 +48,7 @@ class MentorOrder(CommonInfo): ) def __str__(self): - return f"{self.mentor.user.first_name} {self.mentor.user.last_name} | {self.session.course.title}" + return f"{self.mentor.full_name} | {self.session.course.title}" def is_checked_in(self): return self.check_in is not None diff --git a/coderdojochi/notifications.py b/coderdojochi/notifications.py index bd06442e..999b60db 100644 --- a/coderdojochi/notifications.py +++ b/coderdojochi/notifications.py @@ -29,8 +29,8 @@ def __init__(self, mentor): { "type": "section", "fields": [ - {"type": "mrkdwn", "text": f"*Name*: \n{mentor.user.name}"}, - {"type": "mrkdwn", "text": f"*Email*: \n{mentor.user.email}"}, + {"type": "mrkdwn", "text": f"*Name*: \n{mentor.full_name}"}, + {"type": "mrkdwn", "text": f"*Email*: \n{mentor.email}"}, ], }, ] @@ -39,8 +39,8 @@ def __init__(self, mentor): class NewMentorOrderNotification(SlackNotification): def __init__(self, mentor_order): - name = mentor_order.mentor.user.name - email = mentor_order.mentor.user.email + name = mentor_order.mentor.full_name + email = mentor_order.mentor.email location = mentor_order.session.location.name start_date = mentor_order.session.start_date.strftime("%Y-%m-%d") diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 76853718..4ff60e6a 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -90,7 +90,7 @@ def mentor_approve_avatar(request, pk=None): if mentor.background_check: messages.success( request, - f"{mentor.user.first_name} {mentor.user.last_name}'s avatar approved and their account is now public.", + f"{mentor.full_name}'s avatar approved and their account is now public.", ) return redirect(f"{reverse('mentors')}{mentor.id}") @@ -98,10 +98,7 @@ def mentor_approve_avatar(request, pk=None): else: messages.success( request, - ( - f"{mentor.user.first_name}{mentor.user.last_name}'s avatar approved but they have yet " - f"to fill out the 'background search' form." - ), + (f"{mentor.full_name}'s avatar approved but they have yet " f"to fill out the 'background search' form."), ) return redirect("mentors") @@ -125,13 +122,13 @@ def mentor_reject_avatar(request, pk=None): merge_global_data={ "site_url": settings.SITE_URL, }, - recipients=[mentor.user.email], + recipients=[mentor.email], ) messages.warning( request, ( - f"{mentor.user.first_name} {mentor.user.last_name}'s avatar rejected and their account " + f"{mentor.full_name}'s avatar rejected and their account " f"is no longer public. An email notice has been sent to the mentor." ), ) @@ -329,10 +326,7 @@ def session_check_in(request, pk, template_name="session-check-in.html"): else: order.check_in = timezone.now() - if ( - f"{order.guardian.user.first_name} {order.guardian.user.last_name}" - != request.POST["order_alternate_guardian"] - ): + if f"{order.guardian.full_name}" != request.POST["order_alternate_guardian"]: order.alternate_guardian = request.POST["order_alternate_guardian"] order.save() @@ -570,10 +564,10 @@ def session_announce_mentors(request, pk): ) for mentor in mentors: - recipients.append(mentor.user.email) - merge_data[mentor.user.email] = { - "first_name": mentor.user.first_name, - "last_name": mentor.user.last_name, + recipients.append(mentor.email) + merge_data[mentor.email] = { + "first_name": mentor.first_name, + "last_name": mentor.last_name, } email( @@ -634,10 +628,10 @@ def session_announce_guardians(request, pk): ) for guardian in guardians: - recipients.append(guardian.user.email) - merge_data[guardian.user.email] = { - "first_name": guardian.user.first_name, - "last_name": guardian.user.last_name, + recipients.append(guardian.email) + merge_data[guardian.email] = { + "first_name": guardian.first_name, + "last_name": guardian.last_name, } email( diff --git a/coderdojochi/signals_handlers.py b/coderdojochi/signals_handlers.py index ec3bf624..541c408d 100644 --- a/coderdojochi/signals_handlers.py +++ b/coderdojochi/signals_handlers.py @@ -28,11 +28,11 @@ def avatar_updated_handler(sender, instance, **kwargs): img.add_header("Content-Disposition", "inline", filename="avatar") email( - subject=f"{instance.user.first_name} {instance.user.last_name} | Mentor Avatar Changed", + subject=f"{instance.full_name} | Mentor Avatar Changed", template_name="avatar-changed-mentor", merge_global_data={ - "first_name": instance.user.first_name, - "last_name": instance.user.last_name, + "first_name": instance.first_name, + "last_name": instance.last_name, "image": "avatar", "approve_url": f"{settings.SITE_URL}{instance.get_approve_avatar_url()}", "reject_url": f"{settings.SITE_URL}{instance.get_reject_avatar_url()}", diff --git a/coderdojochi/templates/coderdojochi/mentor_detail.html b/coderdojochi/templates/coderdojochi/mentor_detail.html index 783d7126..5150c5f8 100644 --- a/coderdojochi/templates/coderdojochi/mentor_detail.html +++ b/coderdojochi/templates/coderdojochi/mentor_detail.html @@ -1,7 +1,7 @@ {% extends "coderdojochi/_base.html" %} {% load humanize %} -{% block title %}{{ mentor.user.first_name }} {{ mentor.user.last_name }} | {{ block.super }}{% endblock %} +{% block title %}{{ mentor.full_name }} | {{ block.super }}{% endblock %} {% block body_class %}page-mentor-detail{% endblock %} @@ -10,19 +10,19 @@
    -

    {{ mentor.user.first_name }} {{ mentor.user.last_name }}

    +

    {{ mentor.full_name }}

    {{ mentor.bio }}

    - Photo of {{ mentor.user.first_name }} {{ mentor.user.last_name }} + Photo of {{ mentor.full_name }}

    Joined: {{ mentor.user.date_joined|naturalday|capfirst }}

    {% if user.is_staff %} diff --git a/coderdojochi/templates/coderdojochi/mentor_list.html b/coderdojochi/templates/coderdojochi/mentor_list.html index 54d37764..f2d00e5c 100644 --- a/coderdojochi/templates/coderdojochi/mentor_list.html +++ b/coderdojochi/templates/coderdojochi/mentor_list.html @@ -12,9 +12,9 @@

    Meet Our Mentors - Photo of {{ mentor.user.first_name }} {{ mentor.user.last_name }} + Photo of {{ mentor.full_name }} -

    {{ mentor.user.first_name }} {{ mentor.user.last_name }}

    +

    {{ mentor.full_name }}

    {% endfor %}
    diff --git a/coderdojochi/templates/dashboard/meeting-check-in.html b/coderdojochi/templates/dashboard/meeting-check-in.html index 20ae4a87..42e347ce 100644 --- a/coderdojochi/templates/dashboard/meeting-check-in.html +++ b/coderdojochi/templates/dashboard/meeting-check-in.html @@ -159,7 +159,7 @@

    Attending {{ orders.count }}

    {% csrf_token %} {{ forloop.counter|stringformat:"02d" }} - {{ order.mentor.user.first_name }} {{ order.mentor.user.last_name }} + {{ order.mentor.first_name }} {{ order.mentor.last_name }} {% if not order.check_in %} @@ -198,7 +198,7 @@

    Cancelled {{ inactive_orders.count }} {{ forloop.counter|stringformat:"02d" }} - {{ order.mentor.user.first_name }} {{ order.mentor.user.last_name }} + {{ order.mentor.first_name }} {{ order.mentor.last_name }} {{ order.updated_at }} {% endfor %} diff --git a/coderdojochi/templates/dashboard/session-check-in-mentors.html b/coderdojochi/templates/dashboard/session-check-in-mentors.html index 71f2714b..65766783 100644 --- a/coderdojochi/templates/dashboard/session-check-in-mentors.html +++ b/coderdojochi/templates/dashboard/session-check-in-mentors.html @@ -86,9 +86,9 @@

    Attended Mentors {{ active_orders.count }} {{ forloop.counter|stringformat:"02d" }} - {{ order.mentor.user.first_name }} {{ order.mentor.user.last_name }} + {{ order.mentor.first_name }} {{ order.mentor.last_name }} - {{ order.mentor.user.email }} + {{ order.mentor.email }} {{ order.mentor.phone }} - - @@ -145,8 +145,8 @@

    No Shows {{ no_show_orders.count }} {% for order in no_show_orders %} {{ forloop.counter|stringformat:"02d" }} - {{ order.mentor.user.first_name }} {{ order.mentor.user.last_name }} - {{ order.mentor.user.email }} + {{ order.mentor.first_name }} {{ order.mentor.last_name }} + {{ order.mentor.email }} {{ order.mentor.phone }} - - @@ -180,7 +180,7 @@

    Cancelled Tickets {{ inactive_orders.count {{ forloop.counter|stringformat:"02d" }} - {{ order.mentor.user.first_name }} {{ order.mentor.user.last_name }} + {{ order.mentor.first_name }} {{ order.mentor.last_name }} - - diff --git a/coderdojochi/templates/dashboard/session-check-in.html b/coderdojochi/templates/dashboard/session-check-in.html index 78c88ad4..812bbcbd 100644 --- a/coderdojochi/templates/dashboard/session-check-in.html +++ b/coderdojochi/templates/dashboard/session-check-in.html @@ -210,16 +210,16 @@

    Attended Students {{ active_orders.count } {{ order.student | student_age:session.start_date }} {% if active_session %} - + {% else %} {% if order.alternate_guardian %} {{ order.alternate_guardian }} {% else %} - {{ order.guardian.user.first_name }} {{ order.guardian.user.last_name }} + {{ order.guardian.full_name }} {% endif %} {% endif %} - {{ order.guardian.user.email }} + {{ order.guardian.email }} {{ order.guardian.phone }} {{ order.student.school_name }} @@ -273,7 +273,7 @@

    No Shows {{ no_show_orders.count }} {{ order.student.first_name }} {{ order.student.last_name }} {{ order.student | student_age:session.start_date }} - {{ order.guardian.user.first_name }} {{ order.guardian.user.last_name }} + {{ order.guardian.full_name }} {{ order.updated_at }} {{ order.num_attended }} {{ order.num_missed }} @@ -312,8 +312,8 @@

    Cancelled Tickets {{ inactive_orders.count {{ forloop.counter|stringformat:"02d" }} {{ order.student.first_name }} {{ order.student.last_name }} {{ order.student | student_age:session.start_date }} - {{ order.guardian.user.first_name }} {{ order.guardian.user.last_name }} - {{ order.guardian.user.email }} + {{ order.guardian.full_name }} + {{ order.guardian.email }} {{ order.guardian.phone }} {{ order.student.school_name }} {{ order.updated_at }} diff --git a/coderdojochi/templates/dashboard/session-stats.html b/coderdojochi/templates/dashboard/session-stats.html index fdeebd78..8531e8ec 100644 --- a/coderdojochi/templates/dashboard/session-stats.html +++ b/coderdojochi/templates/dashboard/session-stats.html @@ -101,7 +101,7 @@

    All Students

    {% csrf_token %} {{ order.student.last_name }}, {{ order.student.first_name }} - {% if order.alternate_guardian %}{{ order.alternate_guardian }}{% else %}{{ order.guardian.user.first_name }} {{ order.guardian.user.last_name }}{% endif %} + {% if order.alternate_guardian %}{{ order.alternate_guardian }}{% else %}{{ order.guardian.full_name }}{% endif %} {{ order.student | student_age:session.start_date }} {{ order.student.gender }} {% if order.check_in %} diff --git a/coderdojochi/templates/guardian/session-detail.html b/coderdojochi/templates/guardian/session_detail.html similarity index 94% rename from coderdojochi/templates/guardian/session-detail.html rename to coderdojochi/templates/guardian/session_detail.html index 8844e0f3..6d33be2e 100644 --- a/coderdojochi/templates/guardian/session-detail.html +++ b/coderdojochi/templates/guardian/session_detail.html @@ -169,12 +169,14 @@
    Cost

    About the instructor

    - Photo of object.instructor }} + + Photo of {{ object.instructor }} +

    {{ object.instructor }}

    {{ object.instructor.bio|truncatechars_html:120 }}

    -

    Learn more about {{ object.instructor.user.first_name }}.

    +

    Learn more about {{ object.instructor.first_name }}.

@@ -187,7 +189,7 @@

Meet the mentors

{% for mentor in active_mentors %} {% if mentor.is_public == True %}
- {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.avatar.thumbnail %} + {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.get_avatar.thumbnail %}
{% endif %} {% endfor %} diff --git a/coderdojochi/templates/meeting-detail.html b/coderdojochi/templates/meeting-detail.html index 91bfae5b..bdc63400 100644 --- a/coderdojochi/templates/meeting-detail.html +++ b/coderdojochi/templates/meeting-detail.html @@ -47,8 +47,8 @@

Attending

{% if mentor.is_public == True %} {% endif %} diff --git a/coderdojochi/templates/mentor/session-detail.html b/coderdojochi/templates/mentor/session_detail.html similarity index 96% rename from coderdojochi/templates/mentor/session-detail.html rename to coderdojochi/templates/mentor/session_detail.html index 38378996..4eca6b22 100644 --- a/coderdojochi/templates/mentor/session-detail.html +++ b/coderdojochi/templates/mentor/session_detail.html @@ -153,12 +153,14 @@
Cost

About the instructor

- Photo of session.instructor }} + + Photo of {{ object.instructor }} +

{{ session.instructor }}

{{ session.instructor.bio|truncatechars_html:120 }}

-

Learn more about {{ session.instructor.user.first_name }}.

+

Learn more about {{ session.instructor.first_name }}.

@@ -171,7 +173,7 @@

Meet the mentors

{% for mentor in active_mentors %} {% if mentor.is_public == True %}
- {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.avatar.thumbnail %} + {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.get_avatar.thumbnail %}
{% endif %} {% endfor %} diff --git a/coderdojochi/templates/public/session-detail.html b/coderdojochi/templates/public/session_detail.html similarity index 93% rename from coderdojochi/templates/public/session-detail.html rename to coderdojochi/templates/public/session_detail.html index 124e18bb..e692c353 100644 --- a/coderdojochi/templates/public/session-detail.html +++ b/coderdojochi/templates/public/session_detail.html @@ -96,12 +96,14 @@
Cost

About the instructor

- Photo of object.instructor }} + + Photo of {{ object.instructor }} +

{{ object.instructor }}

{{ object.instructor.bio|truncatechars_html:120 }}

-

Learn more about {{ object.instructor.user.first_name }}.

+

Learn more about {{ object.instructor.first_name }}.

@@ -114,7 +116,7 @@

Meet the mentors

{% for mentor in active_mentors %} {% if mentor.is_public == True %}
- {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.avatar.thumbnail %} + {% include "weallcode/snippets/team_member.html" with is_small=True name=mentor.full_name image=mentor.get_avatar.thumbnail %}
{% endif %} {% endfor %} diff --git a/coderdojochi/templates/volunteer.html b/coderdojochi/templates/volunteer.html index 8fa3a4bd..1b9d3ab7 100644 --- a/coderdojochi/templates/volunteer.html +++ b/coderdojochi/templates/volunteer.html @@ -29,12 +29,8 @@

Meet Our Volunteers - {% if mentor.avatar %} - Photo of {{ mentor.user.first_name }} {{ mentor.user.last_name }} - {% else %} - - {% endif %} - {{ mentor.user.first_name }} {{ mentor.user.last_name }} + Photo of {{ mentor.full_name }} + {{ mentor.full_name }} {% endfor %} diff --git a/coderdojochi/views/guardian/sessions.py b/coderdojochi/views/guardian/sessions.py index 6b414e24..24917a23 100644 --- a/coderdojochi/views/guardian/sessions.py +++ b/coderdojochi/views/guardian/sessions.py @@ -6,15 +6,12 @@ class SessionDetailView(DetailView): model = Session - template_name = "guardian/session-detail.html" + template_name = "guardian/session_detail.html" def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - # print(kwargs["object"]) - guardian = get_object_or_404(Guardian, user=self.request.user) + context = super().get_context_data(**kwargs) context["students"] = guardian.get_students() context["spots_remaining"] = self.object.capacity - self.object.get_active_student_count() diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index e768716b..af417b07 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -210,10 +210,10 @@ def meeting_announce(request, pk): recipients = [] for mentor in mentors: - recipients.append(mentor.user.email) - merge_data[mentor.user.email] = { - "first_name": mentor.user.first_name, - "last_name": mentor.user.last_name, + recipients.append(mentor.email) + merge_data[mentor.email] = { + "first_name": mentor.first_name, + "last_name": mentor.last_name, } email( diff --git a/coderdojochi/views/mentor/sessions.py b/coderdojochi/views/mentor/sessions.py index ced081a9..6234f5ed 100644 --- a/coderdojochi/views/mentor/sessions.py +++ b/coderdojochi/views/mentor/sessions.py @@ -6,7 +6,7 @@ class SessionDetailView(DetailView): model = Session - template_name = "mentor/session-detail.html" + template_name = "mentor/session_detail.html" def get_context_data(self, **kwargs): session = self.object diff --git a/coderdojochi/views/profile.py b/coderdojochi/views/profile.py index 525c17ca..6c5f9b58 100644 --- a/coderdojochi/views/profile.py +++ b/coderdojochi/views/profile.py @@ -52,8 +52,8 @@ def get_context_data(self, **kwargs): context["account_complete"] = False if ( - mentor.user.first_name - and mentor.user.last_name + mentor.first_name + and mentor.last_name and mentor.avatar and mentor.background_check and past_sessions.count() > 0 diff --git a/coderdojochi/views/public/sessions.py b/coderdojochi/views/public/sessions.py index fc7d3d7a..86c07ebf 100644 --- a/coderdojochi/views/public/sessions.py +++ b/coderdojochi/views/public/sessions.py @@ -6,7 +6,7 @@ class SessionDetailView(DetailView): model = Session - template_name = "public/session-detail.html" + template_name = "public/session_detail.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index 128cfc9e..333d563b 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -112,7 +112,7 @@ def get(self, request, *args, **kwargs): # class SessionDetailView(RoleRedirectMixin, RoleTemplateMixin, TemplateView): -# template_name = "session-detail.html" +# template_name = "session_detail.html" # def dispatch(self, request, *args, **kwargs): # session_obj = get_object_or_404(Session, id=kwargs["pk"]) diff --git a/coderdojochi/views/welcome.py b/coderdojochi/views/welcome.py index d0d08863..77dddcca 100644 --- a/coderdojochi/views/welcome.py +++ b/coderdojochi/views/welcome.py @@ -26,7 +26,7 @@ def dispatch(self, request, *args, **kwargs): if getattr(request.user, "role", False) == "mentor" and request.method == "GET": mentor = get_object_or_404(Mentor, user=request.user) - if mentor.user.first_name: + if mentor.first_name: if next_url: return redirect(next_url) else: @@ -57,7 +57,7 @@ def get_context_data(self, **kwargs): context["add_student"] = True context["form"] = StudentForm(initial={"guardian": guardian.pk}) - if account.user.first_name and account.get_students(): + if account.first_name and account.get_students(): context["students"] = account.get_students().count() context["account"] = account diff --git a/weallcode/templates/weallcode/snippets/team_member.html b/weallcode/templates/weallcode/snippets/team_member.html index 156fe65b..9d666854 100644 --- a/weallcode/templates/weallcode/snippets/team_member.html +++ b/weallcode/templates/weallcode/snippets/team_member.html @@ -12,8 +12,6 @@ {{ image.url }} {% elif static_image %} {% static static_image %} - {% else %} - https://gravatar.com/avatar/?s=283&d=mm {% endif %}) " > From 2e8252b0426e88625768a6f7114fffa434445c08 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:13:13 -0600 Subject: [PATCH 21/57] meeting-check-in --- coderdojochi/old_views.py | 2 +- .../dashboard/{meeting-check-in.html => meeting_check_in.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/dashboard/{meeting-check-in.html => meeting_check_in.html} (100%) diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 4ff60e6a..d320f494 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -488,7 +488,7 @@ def session_donations(request, pk, template_name="session-donations.html"): @login_required @never_cache -def meeting_check_in(request, meeting_id, template_name="meeting-check-in.html"): +def meeting_check_in(request, meeting_id, template_name="meeting_check_in.html"): if not request.user.is_staff: messages.error(request, "You do not have permission to access this page.") diff --git a/coderdojochi/templates/dashboard/meeting-check-in.html b/coderdojochi/templates/dashboard/meeting_check_in.html similarity index 100% rename from coderdojochi/templates/dashboard/meeting-check-in.html rename to coderdojochi/templates/dashboard/meeting_check_in.html From d46600e0e5747c77b18c4ab71f5b4addd267c4ce Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:14:11 -0600 Subject: [PATCH 22/57] session-check-in-mentors --- coderdojochi/old_views.py | 2 +- ...sion-check-in-mentors.html => session_check_in_mentors.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/dashboard/{session-check-in-mentors.html => session_check_in_mentors.html} (100%) diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index d320f494..2e136aec 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -400,7 +400,7 @@ def session_check_in(request, pk, template_name="session-check-in.html"): @login_required @never_cache -def session_check_in_mentors(request, pk, template_name="session-check-in-mentors.html"): +def session_check_in_mentors(request, pk, template_name="session_check_in_mentors.html"): if not request.user.is_staff: messages.error(request, "You do not have permission to access this page.") diff --git a/coderdojochi/templates/dashboard/session-check-in-mentors.html b/coderdojochi/templates/dashboard/session_check_in_mentors.html similarity index 100% rename from coderdojochi/templates/dashboard/session-check-in-mentors.html rename to coderdojochi/templates/dashboard/session_check_in_mentors.html From 0199fedb53294e0cb258840f048f8bf13a0c66ae Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:14:53 -0600 Subject: [PATCH 23/57] session-check-in --- coderdojochi/old_views.py | 2 +- .../dashboard/{session-check-in.html => session_check_in.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/dashboard/{session-check-in.html => session_check_in.html} (100%) diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 2e136aec..30d20226 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -310,7 +310,7 @@ def session_stats(request, pk, template_name="session-stats.html"): @login_required @never_cache -def session_check_in(request, pk, template_name="session-check-in.html"): +def session_check_in(request, pk, template_name="session_check_in.html"): if not request.user.is_staff: messages.error(request, "You do not have permission to access this page.") diff --git a/coderdojochi/templates/dashboard/session-check-in.html b/coderdojochi/templates/dashboard/session_check_in.html similarity index 100% rename from coderdojochi/templates/dashboard/session-check-in.html rename to coderdojochi/templates/dashboard/session_check_in.html From 493706d727e63a484529ca652a3aa30d9be30bb9 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:16:08 -0600 Subject: [PATCH 24/57] session-donations --- coderdojochi/old_views.py | 2 +- .../{session-donations.html => session_donations.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/dashboard/{session-donations.html => session_donations.html} (100%) diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 30d20226..0b5dfae1 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -458,7 +458,7 @@ def session_check_in_mentors(request, pk, template_name="session_check_in_mentor @login_required @never_cache -def session_donations(request, pk, template_name="session-donations.html"): +def session_donations(request, pk, template_name="session_donations.html"): # TODO: we should really turn this into a decorator if not request.user.is_staff: diff --git a/coderdojochi/templates/dashboard/session-donations.html b/coderdojochi/templates/dashboard/session_donations.html similarity index 100% rename from coderdojochi/templates/dashboard/session-donations.html rename to coderdojochi/templates/dashboard/session_donations.html From b4c54d3602b4a703ae1acff47db6f5ab4c9bbb3a Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:16:40 -0600 Subject: [PATCH 25/57] session-stats --- coderdojochi/old_views.py | 2 +- .../dashboard/{session-stats.html => session_stats.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/dashboard/{session-stats.html => session_stats.html} (100%) diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 0b5dfae1..a933d40d 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -255,7 +255,7 @@ def cdc_admin(request, template_name="admin.html"): @login_required @never_cache -def session_stats(request, pk, template_name="session-stats.html"): +def session_stats(request, pk, template_name="session_stats.html"): if not request.user.is_staff: messages.error(request, "You do not have permission to access this page.") diff --git a/coderdojochi/templates/dashboard/session-stats.html b/coderdojochi/templates/dashboard/session_stats.html similarity index 100% rename from coderdojochi/templates/dashboard/session-stats.html rename to coderdojochi/templates/dashboard/session_stats.html From b28e8717c6ebb77fa92b3006ad5f9013aad65938 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:17:39 -0600 Subject: [PATCH 26/57] session-sign-up --- .../guardian/{session-sign-up.html => session_sign_up.html} | 0 coderdojochi/views/sessions.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/guardian/{session-sign-up.html => session_sign_up.html} (100%) diff --git a/coderdojochi/templates/guardian/session-sign-up.html b/coderdojochi/templates/guardian/session_sign_up.html similarity index 100% rename from coderdojochi/templates/guardian/session-sign-up.html rename to coderdojochi/templates/guardian/session_sign_up.html diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index 333d563b..d8eed5e8 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -216,7 +216,7 @@ def get(self, request, *args, **kwargs): class SessionSignUpView(RoleRedirectMixin, RoleTemplateMixin, TemplateView): - template_name = "session-sign-up.html" + template_name = "session_sign_up.html" @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): From 184953caa3970033ea4a3be6e72f11c656b72ffe Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:20:22 -0600 Subject: [PATCH 27/57] avatar-changed-mentor --- .../{avatar-changed-mentor.html => avatar_changed_mentor.html} | 0 coderdojochi/signals_handlers.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{avatar-changed-mentor.html => avatar_changed_mentor.html} (100%) diff --git a/coderdojochi/emailtemplates/avatar-changed-mentor.html b/coderdojochi/emailtemplates/avatar_changed_mentor.html similarity index 100% rename from coderdojochi/emailtemplates/avatar-changed-mentor.html rename to coderdojochi/emailtemplates/avatar_changed_mentor.html diff --git a/coderdojochi/signals_handlers.py b/coderdojochi/signals_handlers.py index 541c408d..7ee53c33 100644 --- a/coderdojochi/signals_handlers.py +++ b/coderdojochi/signals_handlers.py @@ -29,7 +29,7 @@ def avatar_updated_handler(sender, instance, **kwargs): email( subject=f"{instance.full_name} | Mentor Avatar Changed", - template_name="avatar-changed-mentor", + template_name="avatar_changed_mentor", merge_global_data={ "first_name": instance.first_name, "last_name": instance.last_name, From 0890c22f70636a8b4acd7539154ae1a416a60f6e Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:22:32 -0600 Subject: [PATCH 28/57] mentor-reject-avatar --- ...atar-rejected-mentor.html => avatar_rejected_mentor.html} | 0 coderdojochi/old_views.py | 5 +---- 2 files changed, 1 insertion(+), 4 deletions(-) rename coderdojochi/emailtemplates/{avatar-rejected-mentor.html => avatar_rejected_mentor.html} (100%) diff --git a/coderdojochi/emailtemplates/avatar-rejected-mentor.html b/coderdojochi/emailtemplates/avatar_rejected_mentor.html similarity index 100% rename from coderdojochi/emailtemplates/avatar-rejected-mentor.html rename to coderdojochi/emailtemplates/avatar_rejected_mentor.html diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index a933d40d..1ed0eaf0 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -127,10 +127,7 @@ def mentor_reject_avatar(request, pk=None): messages.warning( request, - ( - f"{mentor.full_name}'s avatar rejected and their account " - f"is no longer public. An email notice has been sent to the mentor." - ), + f"{mentor.full_name}'s avatar rejected and their account is no longer public. An email notice has been sent to the mentor.", ) return redirect("mentors") From 3ecd632164f029b1f028641dc4d827780b080c33 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:23:07 -0600 Subject: [PATCH 29/57] class-announcement-guardian --- ...ouncement-guardian.html => class_announcement_guardian.html} | 0 coderdojochi/old_views.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{class-announcement-guardian.html => class_announcement_guardian.html} (100%) diff --git a/coderdojochi/emailtemplates/class-announcement-guardian.html b/coderdojochi/emailtemplates/class_announcement_guardian.html similarity index 100% rename from coderdojochi/emailtemplates/class-announcement-guardian.html rename to coderdojochi/emailtemplates/class_announcement_guardian.html diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 1ed0eaf0..03d84c51 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -633,7 +633,7 @@ def session_announce_guardians(request, pk): email( subject="New We All Code class date announced!", - template_name="class-announcement-guardian", + template_name="class_announcement_guardian", merge_data=merge_data, merge_global_data=merge_global_data, recipients=recipients, From f21b2b7f0e2f2729cc7298b9882840a7da30a3ea Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:24:39 -0600 Subject: [PATCH 30/57] class-announcement-mentor --- ...nnouncement-mentor.html => class_announcement_mentor.html} | 0 coderdojochi/old_views.py | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename coderdojochi/emailtemplates/{class-announcement-mentor.html => class_announcement_mentor.html} (100%) diff --git a/coderdojochi/emailtemplates/class-announcement-mentor.html b/coderdojochi/emailtemplates/class_announcement_mentor.html similarity index 100% rename from coderdojochi/emailtemplates/class-announcement-mentor.html rename to coderdojochi/emailtemplates/class_announcement_mentor.html diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 03d84c51..8d15a96b 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -118,7 +118,7 @@ def mentor_reject_avatar(request, pk=None): email( subject="Your We All Code avatar...", - template_name="class-announcement-mentor", + template_name="mentor_reject_avatar", merge_global_data={ "site_url": settings.SITE_URL, }, @@ -569,7 +569,7 @@ def session_announce_mentors(request, pk): email( subject="New We All Code class date announced! Come mentor!", - template_name="class-announcement-mentor", + template_name="class_announcement_mentor", merge_data=merge_data, merge_global_data=merge_global_data, recipients=recipients, From 20918da6cbc7afe2217d3ed2b8dcba5c9e12a88b Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:25:05 -0600 Subject: [PATCH 31/57] class-confirm-guardian --- ...{class-confirm-guardian.html => class_confirm_guardian.html} | 0 coderdojochi/views/sessions.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{class-confirm-guardian.html => class_confirm_guardian.html} (100%) diff --git a/coderdojochi/emailtemplates/class-confirm-guardian.html b/coderdojochi/emailtemplates/class_confirm_guardian.html similarity index 100% rename from coderdojochi/emailtemplates/class-confirm-guardian.html rename to coderdojochi/emailtemplates/class_confirm_guardian.html diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index d8eed5e8..4be29338 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -94,7 +94,7 @@ def session_confirm_guardian(request, session_obj, order, student): email( subject=f"Upcoming class confirmation for {student.first_name} {student.last_name}", - template_name="class-confirm-guardian", + template_name="class_confirm_guardian", merge_global_data=merge_global_data, recipients=[request.user.email], preheader="Magical wizards have generated this confirmation. All thanks to the mystical power of coding.", From c42d534c43f6e9f546678901c1f6e0f760815d17 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:27:20 -0600 Subject: [PATCH 32/57] _email-base --- accounts/templates/account/email/_allauth-email-base.html | 2 +- .../emailtemplates/{_email-base.html => _email_base.html} | 0 coderdojochi/emailtemplates/avatar_changed_mentor.html | 2 +- coderdojochi/emailtemplates/avatar_rejected_mentor.html | 2 +- coderdojochi/emailtemplates/class-confirm-mentor.html | 2 +- .../emailtemplates/class-reminder-guardian-24-hour.html | 2 +- .../emailtemplates/class-reminder-guardian-one-week.html | 2 +- coderdojochi/emailtemplates/class-reminder-mentor-24-hour.html | 2 +- coderdojochi/emailtemplates/class-reminder-mentor-one-week.html | 2 +- coderdojochi/emailtemplates/class_announcement_guardian.html | 2 +- coderdojochi/emailtemplates/class_announcement_mentor.html | 2 +- coderdojochi/emailtemplates/class_confirm_guardian.html | 2 +- coderdojochi/emailtemplates/donation-receipt.html | 2 +- coderdojochi/emailtemplates/meeting-announcement-mentor.html | 2 +- coderdojochi/emailtemplates/meeting-confirm-mentor.html | 2 +- coderdojochi/emailtemplates/waitlist-offer-guardian.html | 2 +- coderdojochi/emailtemplates/waitlist-offer-mentor.html | 2 +- coderdojochi/emailtemplates/welcome-guardian.html | 2 +- coderdojochi/emailtemplates/welcome-mentor.html | 2 +- 19 files changed, 18 insertions(+), 18 deletions(-) rename coderdojochi/emailtemplates/{_email-base.html => _email_base.html} (100%) diff --git a/accounts/templates/account/email/_allauth-email-base.html b/accounts/templates/account/email/_allauth-email-base.html index 9dd7f546..df652db8 100644 --- a/accounts/templates/account/email/_allauth-email-base.html +++ b/accounts/templates/account/email/_allauth-email-base.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block subject %}{% block child_subject %}{% endblock %}{% endblock %} {% block preheader %}{% block child_preheader %}{% endblock %}{% endblock %} diff --git a/coderdojochi/emailtemplates/_email-base.html b/coderdojochi/emailtemplates/_email_base.html similarity index 100% rename from coderdojochi/emailtemplates/_email-base.html rename to coderdojochi/emailtemplates/_email_base.html diff --git a/coderdojochi/emailtemplates/avatar_changed_mentor.html b/coderdojochi/emailtemplates/avatar_changed_mentor.html index d390bb8b..80c22805 100644 --- a/coderdojochi/emailtemplates/avatar_changed_mentor.html +++ b/coderdojochi/emailtemplates/avatar_changed_mentor.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %} diff --git a/coderdojochi/emailtemplates/avatar_rejected_mentor.html b/coderdojochi/emailtemplates/avatar_rejected_mentor.html index ed43e311..14c1f0d1 100644 --- a/coderdojochi/emailtemplates/avatar_rejected_mentor.html +++ b/coderdojochi/emailtemplates/avatar_rejected_mentor.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/class-confirm-mentor.html b/coderdojochi/emailtemplates/class-confirm-mentor.html index 8e2bf2ae..c43f615a 100644 --- a/coderdojochi/emailtemplates/class-confirm-mentor.html +++ b/coderdojochi/emailtemplates/class-confirm-mentor.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/class-reminder-guardian-24-hour.html b/coderdojochi/emailtemplates/class-reminder-guardian-24-hour.html index 6eb4dbe0..a96babff 100644 --- a/coderdojochi/emailtemplates/class-reminder-guardian-24-hour.html +++ b/coderdojochi/emailtemplates/class-reminder-guardian-24-hour.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/class-reminder-guardian-one-week.html b/coderdojochi/emailtemplates/class-reminder-guardian-one-week.html index 65204694..5d75f3a8 100644 --- a/coderdojochi/emailtemplates/class-reminder-guardian-one-week.html +++ b/coderdojochi/emailtemplates/class-reminder-guardian-one-week.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/class-reminder-mentor-24-hour.html b/coderdojochi/emailtemplates/class-reminder-mentor-24-hour.html index f4ccbed7..f8bd1b6c 100644 --- a/coderdojochi/emailtemplates/class-reminder-mentor-24-hour.html +++ b/coderdojochi/emailtemplates/class-reminder-mentor-24-hour.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/class-reminder-mentor-one-week.html b/coderdojochi/emailtemplates/class-reminder-mentor-one-week.html index e910b994..38eae0a6 100644 --- a/coderdojochi/emailtemplates/class-reminder-mentor-one-week.html +++ b/coderdojochi/emailtemplates/class-reminder-mentor-one-week.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/class_announcement_guardian.html b/coderdojochi/emailtemplates/class_announcement_guardian.html index f3284240..81ec9680 100644 --- a/coderdojochi/emailtemplates/class_announcement_guardian.html +++ b/coderdojochi/emailtemplates/class_announcement_guardian.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/class_announcement_mentor.html b/coderdojochi/emailtemplates/class_announcement_mentor.html index 2464ffa5..d9266c36 100644 --- a/coderdojochi/emailtemplates/class_announcement_mentor.html +++ b/coderdojochi/emailtemplates/class_announcement_mentor.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/class_confirm_guardian.html b/coderdojochi/emailtemplates/class_confirm_guardian.html index 2a02761d..779fd47d 100644 --- a/coderdojochi/emailtemplates/class_confirm_guardian.html +++ b/coderdojochi/emailtemplates/class_confirm_guardian.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/donation-receipt.html b/coderdojochi/emailtemplates/donation-receipt.html index 2984c72a..3c51b2f0 100644 --- a/coderdojochi/emailtemplates/donation-receipt.html +++ b/coderdojochi/emailtemplates/donation-receipt.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}

Wow, that is so incredibly cool of you. Your generous donation is what makes We All Code possible. The classes, equipment—and let’s not forget the food—are all thanks to contributions like yours.

diff --git a/coderdojochi/emailtemplates/meeting-announcement-mentor.html b/coderdojochi/emailtemplates/meeting-announcement-mentor.html index dde13811..65bb4f3a 100644 --- a/coderdojochi/emailtemplates/meeting-announcement-mentor.html +++ b/coderdojochi/emailtemplates/meeting-announcement-mentor.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/meeting-confirm-mentor.html b/coderdojochi/emailtemplates/meeting-confirm-mentor.html index 159e6415..6aced7dd 100644 --- a/coderdojochi/emailtemplates/meeting-confirm-mentor.html +++ b/coderdojochi/emailtemplates/meeting-confirm-mentor.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/waitlist-offer-guardian.html b/coderdojochi/emailtemplates/waitlist-offer-guardian.html index 81ffe3a6..d84faea6 100644 --- a/coderdojochi/emailtemplates/waitlist-offer-guardian.html +++ b/coderdojochi/emailtemplates/waitlist-offer-guardian.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}

Guardian Waitlist Offer

diff --git a/coderdojochi/emailtemplates/waitlist-offer-mentor.html b/coderdojochi/emailtemplates/waitlist-offer-mentor.html index 4d745b2e..7b4da661 100644 --- a/coderdojochi/emailtemplates/waitlist-offer-mentor.html +++ b/coderdojochi/emailtemplates/waitlist-offer-mentor.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}

Mentor Waitlist Offer

diff --git a/coderdojochi/emailtemplates/welcome-guardian.html b/coderdojochi/emailtemplates/welcome-guardian.html index 5348863b..979c144f 100644 --- a/coderdojochi/emailtemplates/welcome-guardian.html +++ b/coderdojochi/emailtemplates/welcome-guardian.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
diff --git a/coderdojochi/emailtemplates/welcome-mentor.html b/coderdojochi/emailtemplates/welcome-mentor.html index 7ea70ebb..946452e0 100644 --- a/coderdojochi/emailtemplates/welcome-mentor.html +++ b/coderdojochi/emailtemplates/welcome-mentor.html @@ -1,4 +1,4 @@ -{% extends "_email-base.html" %} +{% extends "_email_base.html" %} {% block content %}
From 24dd9f238f25458f2c76c6c9daf3ed66c7408dbf Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:28:03 -0600 Subject: [PATCH 33/57] class-confirm-mentor --- .../{class-confirm-mentor.html => class_confirm_mentor.html} | 0 coderdojochi/views/sessions.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{class-confirm-mentor.html => class_confirm_mentor.html} (100%) diff --git a/coderdojochi/emailtemplates/class-confirm-mentor.html b/coderdojochi/emailtemplates/class_confirm_mentor.html similarity index 100% rename from coderdojochi/emailtemplates/class-confirm-mentor.html rename to coderdojochi/emailtemplates/class_confirm_mentor.html diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index 4be29338..e1c9d426 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -57,7 +57,7 @@ def session_confirm_mentor(request, session_obj, order): subject="Mentoring confirmation for {} class".format( arrow.get(session_obj.mentor_start_date).to("local").format("MMMM D"), ), - template_name="class-confirm-mentor", + template_name="class_confirm_mentor", merge_global_data=merge_global_data, recipients=[request.user.email], preheader="It's time to use your powers for good.", From eca8c596849e1456ff62dbc6edc936b9c5fffcab Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:28:39 -0600 Subject: [PATCH 34/57] class-reminder-guardian-24-hour --- coderdojochi/cron.py | 2 +- ...ardian-24-hour.html => class_reminder_guardian_24_hour.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{class-reminder-guardian-24-hour.html => class_reminder_guardian_24_hour.html} (100%) diff --git a/coderdojochi/cron.py b/coderdojochi/cron.py index cc3bed9d..293162b5 100644 --- a/coderdojochi/cron.py +++ b/coderdojochi/cron.py @@ -123,7 +123,7 @@ def do(self): email( subject="Your class is coming up!", - template_name="class-reminder-guardian-24-hour", + template_name="class_reminder_guardian_24_hour", merge_data=merge_data, recipients=recipients, preheader="Your class is just hours away!", diff --git a/coderdojochi/emailtemplates/class-reminder-guardian-24-hour.html b/coderdojochi/emailtemplates/class_reminder_guardian_24_hour.html similarity index 100% rename from coderdojochi/emailtemplates/class-reminder-guardian-24-hour.html rename to coderdojochi/emailtemplates/class_reminder_guardian_24_hour.html From bca000ab3da7d00ea011426106bd42aa15cae448 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:29:47 -0600 Subject: [PATCH 35/57] class-reminder-guardian-one-week --- coderdojochi/cron.py | 2 +- ...dian-one-week.html => class_reminder_guardian_one_week.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{class-reminder-guardian-one-week.html => class_reminder_guardian_one_week.html} (100%) diff --git a/coderdojochi/cron.py b/coderdojochi/cron.py index 293162b5..17ee2ae8 100644 --- a/coderdojochi/cron.py +++ b/coderdojochi/cron.py @@ -77,7 +77,7 @@ def do(self): email( subject="Upcoming class reminder", - template_name="class-reminder-guardian-one-week", + template_name="class_reminder_guardian_one_week", merge_data=merge_data, recipients=recipients, preheader="Your class is just a few days away!", diff --git a/coderdojochi/emailtemplates/class-reminder-guardian-one-week.html b/coderdojochi/emailtemplates/class_reminder_guardian_one_week.html similarity index 100% rename from coderdojochi/emailtemplates/class-reminder-guardian-one-week.html rename to coderdojochi/emailtemplates/class_reminder_guardian_one_week.html From f34a962fae5f51e1cd851c15afad0b3040274b9b Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:30:18 -0600 Subject: [PATCH 36/57] class-reminder-mentor-24-hour --- coderdojochi/cron.py | 2 +- ...r-mentor-24-hour.html => class_reminder_mentor_24_hour.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{class-reminder-mentor-24-hour.html => class_reminder_mentor_24_hour.html} (100%) diff --git a/coderdojochi/cron.py b/coderdojochi/cron.py index 17ee2ae8..e7d0717d 100644 --- a/coderdojochi/cron.py +++ b/coderdojochi/cron.py @@ -216,7 +216,7 @@ def do(self): email( subject="Your We All Code class is tomorrow!", - template_name="class-reminder-mentor-24-hour", + template_name="class_reminder_mentor_24_hour", merge_data=merge_data, recipients=recipients, preheader="The class is just a few hours away!", diff --git a/coderdojochi/emailtemplates/class-reminder-mentor-24-hour.html b/coderdojochi/emailtemplates/class_reminder_mentor_24_hour.html similarity index 100% rename from coderdojochi/emailtemplates/class-reminder-mentor-24-hour.html rename to coderdojochi/emailtemplates/class_reminder_mentor_24_hour.html From 4c2eaaf66ce44b754901385293af3b239e4de82b Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:30:44 -0600 Subject: [PATCH 37/57] class-reminder-mentor-one-week --- coderdojochi/cron.py | 2 +- ...mentor-one-week.html => class_reminder_mentor_one_week.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{class-reminder-mentor-one-week.html => class_reminder_mentor_one_week.html} (100%) diff --git a/coderdojochi/cron.py b/coderdojochi/cron.py index e7d0717d..a3be15dc 100644 --- a/coderdojochi/cron.py +++ b/coderdojochi/cron.py @@ -172,7 +172,7 @@ def do(self): email( subject="Your We All Code class is in less than a week!", - template_name="class-reminder-mentor-one-week", + template_name="class_reminder_mentor_one_week", merge_data=merge_data, recipients=recipients, preheader="The class is just a few days away!", diff --git a/coderdojochi/emailtemplates/class-reminder-mentor-one-week.html b/coderdojochi/emailtemplates/class_reminder_mentor_one_week.html similarity index 100% rename from coderdojochi/emailtemplates/class-reminder-mentor-one-week.html rename to coderdojochi/emailtemplates/class_reminder_mentor_one_week.html From 159d975928ab2add6babdf52d32cb0c604a35460 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:31:11 -0600 Subject: [PATCH 38/57] contact-email --- .../emailtemplates/{contact-email.html => contact_email.html} | 0 weallcode/forms.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{contact-email.html => contact_email.html} (100%) diff --git a/coderdojochi/emailtemplates/contact-email.html b/coderdojochi/emailtemplates/contact_email.html similarity index 100% rename from coderdojochi/emailtemplates/contact-email.html rename to coderdojochi/emailtemplates/contact_email.html diff --git a/weallcode/forms.py b/weallcode/forms.py index a156a391..46e01dbf 100644 --- a/weallcode/forms.py +++ b/weallcode/forms.py @@ -90,7 +90,7 @@ def send_email(self): subject=f"{data['name']} | We All Code Contact Form", recipients=[settings.CONTACT_EMAIL], reply_to=[f"{data['name']}<{data['email']}>"], - template_name="contact-email", + template_name="contact_email", merge_global_data={ "interest": data["interest"], "message": data["message"], From da53ca16564e576f4f417eb89a1d6344ac8165ca Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:32:40 -0600 Subject: [PATCH 39/57] donation_receipt --- .../{donation-receipt.html => donation_receipt.html} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename coderdojochi/emailtemplates/{donation-receipt.html => donation_receipt.html} (100%) diff --git a/coderdojochi/emailtemplates/donation-receipt.html b/coderdojochi/emailtemplates/donation_receipt.html similarity index 100% rename from coderdojochi/emailtemplates/donation-receipt.html rename to coderdojochi/emailtemplates/donation_receipt.html From d8567198cf847a940acb208e8e4f0ff8ec829520 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:33:07 -0600 Subject: [PATCH 40/57] meeting-announcement-mentor --- ...nnouncement-mentor.html => meeting_announcement_mentor.html} | 0 coderdojochi/views/meetings.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{meeting-announcement-mentor.html => meeting_announcement_mentor.html} (100%) diff --git a/coderdojochi/emailtemplates/meeting-announcement-mentor.html b/coderdojochi/emailtemplates/meeting_announcement_mentor.html similarity index 100% rename from coderdojochi/emailtemplates/meeting-announcement-mentor.html rename to coderdojochi/emailtemplates/meeting_announcement_mentor.html diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index af417b07..de1f7a30 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -218,7 +218,7 @@ def meeting_announce(request, pk): email( subject="New meeting announced!", - template_name="meeting-announcement-mentor", + template_name="meeting_announcement_mentor", merge_data=merge_data, merge_global_data=merge_global_data, recipients=recipients, From cdba152c63215747f5f773908ee0732552f64809 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:33:33 -0600 Subject: [PATCH 41/57] meeting-confirm-mentor --- ...{meeting-confirm-mentor.html => meeting_confirm_mentor.html} | 0 coderdojochi/views/meetings.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{meeting-confirm-mentor.html => meeting_confirm_mentor.html} (100%) diff --git a/coderdojochi/emailtemplates/meeting-confirm-mentor.html b/coderdojochi/emailtemplates/meeting_confirm_mentor.html similarity index 100% rename from coderdojochi/emailtemplates/meeting-confirm-mentor.html rename to coderdojochi/emailtemplates/meeting_confirm_mentor.html diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index de1f7a30..0450ff0c 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -163,7 +163,7 @@ def meeting_sign_up(request, pk, template_name="meeting-sign-up.html"): email( subject="Upcoming mentor meeting confirmation", - template_name="meeting-confirm-mentor", + template_name="meeting_confirm_mentor", merge_global_data=merge_global_data, recipients=[request.user.email], preheader=( From cd3277c880d95a83f39a56209dbc5f624dd03fc0 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:47:04 -0600 Subject: [PATCH 42/57] waitlist-offer-guardian.html --- ...{waitlist-offer-guardian.html => waitlist_offer_guardian.html} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename coderdojochi/emailtemplates/{waitlist-offer-guardian.html => waitlist_offer_guardian.html} (100%) diff --git a/coderdojochi/emailtemplates/waitlist-offer-guardian.html b/coderdojochi/emailtemplates/waitlist_offer_guardian.html similarity index 100% rename from coderdojochi/emailtemplates/waitlist-offer-guardian.html rename to coderdojochi/emailtemplates/waitlist_offer_guardian.html From 8c0b94835cedb276a65f7e211f7cf545e2db2c3a Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:47:42 -0600 Subject: [PATCH 43/57] waitlist-offer-mentor --- .../{waitlist-offer-mentor.html => waitlist_offer_mentor.html} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename coderdojochi/emailtemplates/{waitlist-offer-mentor.html => waitlist_offer_mentor.html} (100%) diff --git a/coderdojochi/emailtemplates/waitlist-offer-mentor.html b/coderdojochi/emailtemplates/waitlist_offer_mentor.html similarity index 100% rename from coderdojochi/emailtemplates/waitlist-offer-mentor.html rename to coderdojochi/emailtemplates/waitlist_offer_mentor.html From 45691e454391ffb6fc9a59b004a567885e0f7038 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:48:52 -0600 Subject: [PATCH 44/57] welcome-{role} --- .../{welcome-guardian.html => welcome_guardian.html} | 0 .../emailtemplates/{welcome-mentor.html => welcome_mentor.html} | 0 coderdojochi/views/welcome.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/emailtemplates/{welcome-guardian.html => welcome_guardian.html} (100%) rename coderdojochi/emailtemplates/{welcome-mentor.html => welcome_mentor.html} (100%) diff --git a/coderdojochi/emailtemplates/welcome-guardian.html b/coderdojochi/emailtemplates/welcome_guardian.html similarity index 100% rename from coderdojochi/emailtemplates/welcome-guardian.html rename to coderdojochi/emailtemplates/welcome_guardian.html diff --git a/coderdojochi/emailtemplates/welcome-mentor.html b/coderdojochi/emailtemplates/welcome_mentor.html similarity index 100% rename from coderdojochi/emailtemplates/welcome-mentor.html rename to coderdojochi/emailtemplates/welcome_mentor.html diff --git a/coderdojochi/views/welcome.py b/coderdojochi/views/welcome.py index 77dddcca..3418ff71 100644 --- a/coderdojochi/views/welcome.py +++ b/coderdojochi/views/welcome.py @@ -172,7 +172,7 @@ def create_new_user(self, request, user, next_url): email( subject="Welcome!", - template_name=f"welcome-{role}", + template_name=f"welcome_{role}", merge_global_data=merge_global_data, recipients=[user.email], preheader="Your adventure awaits!", From ce729a915f84d7ae7065744314271175f0057250 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:53:52 -0600 Subject: [PATCH 45/57] _admin-base --- .../templates/dashboard/{_admin-base.html => _admin_base.html} | 0 coderdojochi/templates/dashboard/admin.html | 2 +- coderdojochi/templates/dashboard/meeting_check_in.html | 2 +- coderdojochi/templates/dashboard/session_check_in.html | 2 +- coderdojochi/templates/dashboard/session_check_in_mentors.html | 2 +- coderdojochi/templates/dashboard/session_donations.html | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename coderdojochi/templates/dashboard/{_admin-base.html => _admin_base.html} (100%) diff --git a/coderdojochi/templates/dashboard/_admin-base.html b/coderdojochi/templates/dashboard/_admin_base.html similarity index 100% rename from coderdojochi/templates/dashboard/_admin-base.html rename to coderdojochi/templates/dashboard/_admin_base.html diff --git a/coderdojochi/templates/dashboard/admin.html b/coderdojochi/templates/dashboard/admin.html index 2cbfdf21..d76de3c6 100644 --- a/coderdojochi/templates/dashboard/admin.html +++ b/coderdojochi/templates/dashboard/admin.html @@ -1,4 +1,4 @@ -{% extends "_admin-base.html" %} +{% extends "_admin_base.html" %} {% load i18n coderdojochi_extras %} diff --git a/coderdojochi/templates/dashboard/meeting_check_in.html b/coderdojochi/templates/dashboard/meeting_check_in.html index 42e347ce..913b84db 100644 --- a/coderdojochi/templates/dashboard/meeting_check_in.html +++ b/coderdojochi/templates/dashboard/meeting_check_in.html @@ -1,4 +1,4 @@ -{% extends "_admin-base.html" %} +{% extends "_admin_base.html" %} {% load i18n humanize coderdojochi_extras %} diff --git a/coderdojochi/templates/dashboard/session_check_in.html b/coderdojochi/templates/dashboard/session_check_in.html index 812bbcbd..105feb18 100644 --- a/coderdojochi/templates/dashboard/session_check_in.html +++ b/coderdojochi/templates/dashboard/session_check_in.html @@ -1,4 +1,4 @@ -{% extends "_admin-base.html" %} +{% extends "_admin_base.html" %} {% load i18n humanize coderdojochi_extras %} diff --git a/coderdojochi/templates/dashboard/session_check_in_mentors.html b/coderdojochi/templates/dashboard/session_check_in_mentors.html index 65766783..c105d7a9 100644 --- a/coderdojochi/templates/dashboard/session_check_in_mentors.html +++ b/coderdojochi/templates/dashboard/session_check_in_mentors.html @@ -1,4 +1,4 @@ -{% extends "_admin-base.html" %} +{% extends "_admin_base.html" %} {% load i18n humanize coderdojochi_extras %} diff --git a/coderdojochi/templates/dashboard/session_donations.html b/coderdojochi/templates/dashboard/session_donations.html index faea7c9a..bd361b31 100644 --- a/coderdojochi/templates/dashboard/session_donations.html +++ b/coderdojochi/templates/dashboard/session_donations.html @@ -1,4 +1,4 @@ -{% extends "_admin-base.html" %} +{% extends "_admin_base.html" %} {% load i18n bootstrap3 %} From a96e5c8eae4cc57113868823529e8cc7ce9a0407 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:56:49 -0600 Subject: [PATCH 46/57] meeting-detail --- .../templates/{meeting-detail.html => meeting_detail.html} | 0 coderdojochi/views/meetings.py | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename coderdojochi/templates/{meeting-detail.html => meeting_detail.html} (100%) diff --git a/coderdojochi/templates/meeting-detail.html b/coderdojochi/templates/meeting_detail.html similarity index 100% rename from coderdojochi/templates/meeting-detail.html rename to coderdojochi/templates/meeting_detail.html diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index 0450ff0c..fa5267ca 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -39,7 +39,7 @@ def get_queryset(self): class MeetingDetailView(DetailView): model = Meeting - template_name = "meeting-detail.html" + template_name = "meeting_detail.html" def get_queryset(self): objects = self.model.objects.filter() @@ -172,7 +172,7 @@ def meeting_sign_up(request, pk, template_name="meeting-sign-up.html"): ), ) - return redirect("meeting-detail", meeting_obj.id) + return redirect("meeting_detail", meeting_obj.id) return render(request, template_name, {"meeting": meeting_obj, "user_signed_up": user_signed_up}) From d644efd33e53696559c7a577ba4b2ac1c29e9832 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:57:17 -0600 Subject: [PATCH 47/57] meeting-sign-up --- .../templates/{meeting-sign-up.html => meeting_sign_up.html} | 0 coderdojochi/views/meetings.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/{meeting-sign-up.html => meeting_sign_up.html} (100%) diff --git a/coderdojochi/templates/meeting-sign-up.html b/coderdojochi/templates/meeting_sign_up.html similarity index 100% rename from coderdojochi/templates/meeting-sign-up.html rename to coderdojochi/templates/meeting_sign_up.html diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index fa5267ca..464da0e0 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -103,7 +103,7 @@ def get_location(self, request, event_obj): pass -def meeting_sign_up(request, pk, template_name="meeting-sign-up.html"): +def meeting_sign_up(request, pk, template_name="meeting_sign_up.html"): meeting_obj = get_object_or_404(Meeting, pk=pk) mentor = get_object_or_404(Mentor, user=request.user) From a8b3474e4aef8355a3393876fdd005679b7f0f4e Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:58:29 -0600 Subject: [PATCH 48/57] session-sign-up --- .../mentor/{session-sign-up.html => session_sign_up.html} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename coderdojochi/templates/mentor/{session-sign-up.html => session_sign_up.html} (100%) diff --git a/coderdojochi/templates/mentor/session-sign-up.html b/coderdojochi/templates/mentor/session_sign_up.html similarity index 100% rename from coderdojochi/templates/mentor/session-sign-up.html rename to coderdojochi/templates/mentor/session_sign_up.html From c236edbb8292009a96b1db4c583ca865536143dc Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:58:52 -0600 Subject: [PATCH 49/57] session-partner-password --- ...sion-partner-password.html => session_partner_password.html} | 0 coderdojochi/views/sessions.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/{session-partner-password.html => session_partner_password.html} (100%) diff --git a/coderdojochi/templates/session-partner-password.html b/coderdojochi/templates/session_partner_password.html similarity index 100% rename from coderdojochi/templates/session-partner-password.html rename to coderdojochi/templates/session_partner_password.html diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index e1c9d426..08a4114f 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -344,7 +344,7 @@ def post(self, request, *args, **kwargs): class PasswordSessionView(TemplateView): - template_name = "session-partner-password.html" + template_name = "session_partner_password.html" def get_context_data(self, **kwargs): context = super(PasswordSessionView, self).get_context_data(**kwargs) From b9d214ffc8c1bacd9e52fa7d53818f5df6360c28 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 11:59:22 -0600 Subject: [PATCH 50/57] student-detail --- coderdojochi/old_views.py | 2 +- .../templates/{student-detail.html => student_detail.html} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename coderdojochi/templates/{student-detail.html => student_detail.html} (100%) diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 8d15a96b..5f96d282 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -134,7 +134,7 @@ def mentor_reject_avatar(request, pk=None): @login_required -def student_detail(request, student_id=False, template_name="student-detail.html"): +def student_detail(request, student_id=False, template_name="student_detail.html"): access = True if request.user.role == "guardian" and student_id: diff --git a/coderdojochi/templates/student-detail.html b/coderdojochi/templates/student_detail.html similarity index 100% rename from coderdojochi/templates/student-detail.html rename to coderdojochi/templates/student_detail.html From 6590867dc3c63cf13dd5b2639eb0ffb9bbdba965 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 12:00:15 -0600 Subject: [PATCH 51/57] programs-summer-camps --- .../{programs-summer-camps.html => programs_summer_camps.html} | 0 weallcode/views/programs_summer_camps.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename weallcode/templates/weallcode/{programs-summer-camps.html => programs_summer_camps.html} (100%) diff --git a/weallcode/templates/weallcode/programs-summer-camps.html b/weallcode/templates/weallcode/programs_summer_camps.html similarity index 100% rename from weallcode/templates/weallcode/programs-summer-camps.html rename to weallcode/templates/weallcode/programs_summer_camps.html diff --git a/weallcode/views/programs_summer_camps.py b/weallcode/views/programs_summer_camps.py index 11216b3d..182f12d7 100644 --- a/weallcode/views/programs_summer_camps.py +++ b/weallcode/views/programs_summer_camps.py @@ -5,7 +5,7 @@ class ProgramsSummerCampsView(DefaultMetaTags, TemplateView): - template_name = "weallcode/programs-summer-camps.html" + template_name = "weallcode/programs_summer_camps.html" url = reverse_lazy("weallcode-programs-summer-camps") title = f"Summer Camps | We All Code" From f1dec0e4c3634fc88f11147b1306630c41adeeec Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 12:01:32 -0600 Subject: [PATCH 52/57] _allauth-email-base --- .../{_allauth-email-base.html => _allauth_email_base.html} | 0 .../templates/account/email/email_confirmation_message.html | 2 +- .../templates/account/email/password_reset_key_message.html | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename accounts/templates/account/email/{_allauth-email-base.html => _allauth_email_base.html} (100%) diff --git a/accounts/templates/account/email/_allauth-email-base.html b/accounts/templates/account/email/_allauth_email_base.html similarity index 100% rename from accounts/templates/account/email/_allauth-email-base.html rename to accounts/templates/account/email/_allauth_email_base.html diff --git a/accounts/templates/account/email/email_confirmation_message.html b/accounts/templates/account/email/email_confirmation_message.html index 170a14bf..87b7e835 100644 --- a/accounts/templates/account/email/email_confirmation_message.html +++ b/accounts/templates/account/email/email_confirmation_message.html @@ -1,4 +1,4 @@ -{% extends "account/email/_allauth-email-base.html" %} +{% extends "account/email/_allauth_email_base.html" %} {% load account i18n %} {% user_display user as user_display %} diff --git a/accounts/templates/account/email/password_reset_key_message.html b/accounts/templates/account/email/password_reset_key_message.html index a74c102c..783a9bc1 100644 --- a/accounts/templates/account/email/password_reset_key_message.html +++ b/accounts/templates/account/email/password_reset_key_message.html @@ -1,4 +1,4 @@ -{% extends "account/email/_allauth-email-base.html" %} +{% extends "account/email/_allauth_email_base.html" %} {% load i18n %} From 9237ff16bbcfc8cc10582f10e67f582af226b4a2 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Fri, 15 Jan 2021 12:10:40 -0600 Subject: [PATCH 53/57] Format --- coderdojochi/models/course.py | 4 +-- coderdojochi/models/meeting.py | 4 +-- coderdojochi/old_views.py | 2 +- .../templatetags/coderdojochi_extras.py | 34 +++++-------------- coderdojochi/views/meetings.py | 5 +-- coderdojochi/views/sessions.py | 11 ++---- coderdojochi/views/welcome.py | 1 + 7 files changed, 18 insertions(+), 43 deletions(-) diff --git a/coderdojochi/models/course.py b/coderdojochi/models/course.py index d7431993..4a90d5e0 100644 --- a/coderdojochi/models/course.py +++ b/coderdojochi/models/course.py @@ -61,5 +61,5 @@ class Course(CommonInfo): def __str__(self): if self.code: return f"{self.code} | {self.title}" - else: - return f"{self.title}" + + return f"{self.title}" diff --git a/coderdojochi/models/meeting.py b/coderdojochi/models/meeting.py index 01588efe..91af8780 100644 --- a/coderdojochi/models/meeting.py +++ b/coderdojochi/models/meeting.py @@ -30,8 +30,8 @@ class MeetingType(CommonInfo): def __str__(self): if self.code: return f"{self.code} | {self.title}" - else: - return f"{self.title}" + + return f"{self.title}" class Meeting(CommonInfo): diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 5f96d282..90924611 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -98,7 +98,7 @@ def mentor_approve_avatar(request, pk=None): else: messages.success( request, - (f"{mentor.full_name}'s avatar approved but they have yet " f"to fill out the 'background search' form."), + f"{mentor.full_name}'s avatar approved but they have yet to fill out the 'background search' form.", ) return redirect("mentors") diff --git a/coderdojochi/templatetags/coderdojochi_extras.py b/coderdojochi/templatetags/coderdojochi_extras.py index 34540c9f..ae96e776 100644 --- a/coderdojochi/templatetags/coderdojochi_extras.py +++ b/coderdojochi/templatetags/coderdojochi_extras.py @@ -54,26 +54,15 @@ def student_register_link(context, student, session): session.minimum_age, session.maximum_age, session.start_date ) and not student.is_within_gender_limitation(session.gender_limitation): title = "Limited event." - message = ( - f"Sorry, this class is limited to {session.gender_limitation}s between {session.minimum_age} " - f"and {session.maximum_age} this time around." - ) + message = f"Sorry, this class is limited to {session.gender_limitation}s between {session.minimum_age} and {session.maximum_age} this time around." - button_href = ( - 'data-trigger="hover" data-placement="top" data-toggle="popover" title="" ' - f'data-content="{message}" data-original-title="{title}"' - ) + button_href = f'data-trigger="hover" data-placement="top" data-toggle="popover" title="" data-content="{message}" data-original-title="{title}"' elif not student.is_within_age_range(session.minimum_age, session.maximum_age, session.start_date): title = "Age-limited event." - message = ( - f"Sorry, this class is limited to student between ages {session.minimum_age} and " - f"{session.maximum_age} this time around." - ) - button_href = ( - 'data-trigger="hover" data-placement="top" data-toggle="popover" title="" ' - f'data-content="{message}" data-original-title="{title}"' - ) + message = f"Sorry, this class is limited to student between ages {session.minimum_age} and {session.maximum_age} this time around." + + button_href = f'data-trigger="hover" data-placement="top" data-toggle="popover" title="" data-content="{message}" data-original-title="{title}"' elif not student.is_within_gender_limitation(session.gender_limitation): if session.gender_limitation == "female": @@ -82,16 +71,9 @@ def student_register_link(context, student, session): title = "Boys-only event." message = f"Sorry, this class is limited to {session.gender_limitation}s this time around." - button_href = ( - 'data-trigger="hover" data-placement="top" data-toggle="popover" title="" ' - f'data-content="{message}" data-original-title="{title}" ' - ) - - form = ( - f"<{button_tag} {button_href} class='button small {button_modifier}' {button_additional_attributes}>" - f"{button_msg}" - f"" - ) + button_href = f'data-trigger="hover" data-placement="top" data-toggle="popover" title="" data-content="{message}" data-original-title="{title}" ' + + form = f"<{button_tag} {button_href} class='button small {button_modifier}' {button_additional_attributes}>{button_msg}" return Template(form).render(context) diff --git a/coderdojochi/views/meetings.py b/coderdojochi/views/meetings.py index 464da0e0..79e582c4 100644 --- a/coderdojochi/views/meetings.py +++ b/coderdojochi/views/meetings.py @@ -166,10 +166,7 @@ def meeting_sign_up(request, pk, template_name="meeting_sign_up.html"): template_name="meeting_confirm_mentor", merge_global_data=merge_global_data, recipients=[request.user.email], - preheader=( - f"Thanks for signing up for our next meeting, {request.user.first_name}. " - f"We look forward to seeing there." - ), + preheader=f"Thanks for signing up for our next meeting, {request.user.first_name}. We look forward to seeing there.", ) return redirect("meeting_detail", meeting_obj.id) diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index 08a4114f..cb51d180 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -266,6 +266,7 @@ def check_access(self, request, *args, **kwargs): limits = self.student_limitations(kwargs["student"], kwargs["session_obj"], kwargs["user_signed_up"]) if limits: access_dict = {"message": limits, "redirect": kwargs["session_obj"].get_absolute_url()} + return access_dict def student_limitations(self, student, session_obj, user_signed_up): @@ -273,10 +274,7 @@ def student_limitations(self, student, session_obj, user_signed_up): return f"Sorry, this class is limited to {session_obj.gender_limitation}s this time around." if not student.is_within_age_range(session_obj.minimum_age, session_obj.maximum_age): - return ( - f"Sorry, this class is limited to students between ages " - f"{session_obj.minimum_age} and {session_obj.maximum_age}." - ) + return f"Sorry, this class is limited to students between ages {session_obj.minimum_age} and {session_obj.maximum_age}." if not user_signed_up and session_obj.capacity <= session_obj.get_active_student_count(): return "Sorry this class has sold out. Please sign up for the wait list and/or check back later." @@ -449,9 +447,6 @@ def get_location(self, request, event_obj): pass elif event_obj.location.address: - location = ( - f"{event_obj.location.name}, {event_obj.location.address}, " - f"{event_obj.location.city}, {event_obj.location.state}, {event_obj.location.zip}" - ) + location = f"{event_obj.location.name}, {event_obj.location.address}, {event_obj.location.city}, {event_obj.location.state}, {event_obj.location.zip}" return location diff --git a/coderdojochi/views/welcome.py b/coderdojochi/views/welcome.py index 3418ff71..219a8573 100644 --- a/coderdojochi/views/welcome.py +++ b/coderdojochi/views/welcome.py @@ -157,6 +157,7 @@ def create_new_user(self, request, user, next_url): merge_global_data[ "next_intro_meeting_calendar_url" ] = f"{settings.SITE_URL}{next_meeting.get_calendar_url()}" + if not next_url: next_url = reverse("account_home") else: From 65720e5e7f190ecf98cae4af7806b4d4ab8c8315 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Mon, 18 Jan 2021 17:51:04 -0600 Subject: [PATCH 54/57] save db_views into fixtures --- .../db_views/view_coderdojochi_donation.sql | 53 +++++++++++ .../view_coderdojochi_mentororder.sql | 23 +++++ fixtures/db_views/view_coderdojochi_order.sql | 37 ++++++++ .../db_views/view_coderdojochi_session.sql | 26 ++++++ .../view_coderdojochi_session_w_signups.sql | 62 +++++++++++++ .../db_views/view_coderdojochi_student.sql | 55 +++++++++++ .../view_coderdojochi_student_w_eth.sql | 33 +++++++ fixtures/db_views/view_school_names.sql | 9 ++ .../view_student_race_ethnicity_unpivot.sql | 57 ++++++++++++ fixtures/db_views/view_weallcode_mentor.sql | 92 +++++++++++++++++++ 10 files changed, 447 insertions(+) create mode 100644 fixtures/db_views/view_coderdojochi_donation.sql create mode 100644 fixtures/db_views/view_coderdojochi_mentororder.sql create mode 100644 fixtures/db_views/view_coderdojochi_order.sql create mode 100644 fixtures/db_views/view_coderdojochi_session.sql create mode 100644 fixtures/db_views/view_coderdojochi_session_w_signups.sql create mode 100644 fixtures/db_views/view_coderdojochi_student.sql create mode 100644 fixtures/db_views/view_coderdojochi_student_w_eth.sql create mode 100644 fixtures/db_views/view_school_names.sql create mode 100644 fixtures/db_views/view_student_race_ethnicity_unpivot.sql create mode 100644 fixtures/db_views/view_weallcode_mentor.sql diff --git a/fixtures/db_views/view_coderdojochi_donation.sql b/fixtures/db_views/view_coderdojochi_donation.sql new file mode 100644 index 00000000..513a67e4 --- /dev/null +++ b/fixtures/db_views/view_coderdojochi_donation.sql @@ -0,0 +1,53 @@ +SELECT + a.source, + a.id, + a.address_country, + a.address_city, + a.address_name, + a.address_state, + a.address_zip, + a.first_name, + a.last_name, + a.payer_business_name, + a.mc_fee, + a.mc_gross, + a.payment_date +FROM + ( + SELECT + 'paypal' :: text AS source, + paypal_ipn.id, + paypal_ipn.address_country, + paypal_ipn.address_city, + paypal_ipn.address_name, + paypal_ipn.address_state, + paypal_ipn.address_zip, + paypal_ipn.first_name, + paypal_ipn.last_name, + paypal_ipn.payer_business_name, + paypal_ipn.mc_fee, + paypal_ipn.mc_gross, + paypal_ipn.payment_date + FROM + paypal_ipn + UNION + ALL + SELECT + 'cdc' :: text AS source, + coderdojochi_donation.id, + NULL :: character varying AS "varchar", + NULL :: character varying AS "varchar", + NULL :: character varying AS "varchar", + NULL :: character varying AS "varchar", + NULL :: character varying AS "varchar", + coderdojochi_donation.first_name, + coderdojochi_donation.last_name, + NULL :: character varying AS "varchar", + NULL :: numeric AS "numeric", + coderdojochi_donation.amount, + coderdojochi_donation.created_at + FROM + coderdojochi_donation + ) a +ORDER BY + a.payment_date; diff --git a/fixtures/db_views/view_coderdojochi_mentororder.sql b/fixtures/db_views/view_coderdojochi_mentororder.sql new file mode 100644 index 00000000..ea98da1b --- /dev/null +++ b/fixtures/db_views/view_coderdojochi_mentororder.sql @@ -0,0 +1,23 @@ +SELECT + m.mentor_id, + m.user_id, + m.mentor_name, + m.background_check, + mo.check_in, + mo.created_at AS sign_up_time, + mo.session_id, + s.code, + s.title, + s.start_date, + s.end_date, + date_part('hour' :: text, (s.end_date - s.start_date)) AS class_hours +FROM + ( + ( + coderdojochi_mentororder mo + JOIN view_weallcode_mentor m ON ((mo.mentor_id = m.mentor_id)) + ) + JOIN view_coderdojochi_session s ON ((mo.session_id = s.id)) + ) +WHERE + (mo.is_active = true); diff --git a/fixtures/db_views/view_coderdojochi_order.sql b/fixtures/db_views/view_coderdojochi_order.sql new file mode 100644 index 00000000..23f91657 --- /dev/null +++ b/fixtures/db_views/view_coderdojochi_order.sql @@ -0,0 +1,37 @@ +SELECT + o.id, + o.session_id, + o.student_id, + o.is_active, + o.check_in, + o.created_at, + s.first_name, + s.last_name, + s.guardian_zip, + s.age, + s.gender, + s.school_name, + s.school_type, + s.eth_american_indian, + s.eth_asian, + s.eth_arab, + s.eth_black_african_american, + s.eth_hispanic_latino, + s.eth_pacific_islander, + s.eth_white, + s.eth_not_list, + ses.code, + ses.title, + ses.start_date, + ses.end_date, + ses.name +FROM + ( + ( + coderdojochi_order o + LEFT JOIN view_coderdojochi_session ses ON ((o.session_id = ses.id)) + ) + LEFT JOIN view_coderdojochi_student_w_eth s ON ((o.student_id = s.id)) + ) +WHERE + (o.is_active = true); diff --git a/fixtures/db_views/view_coderdojochi_session.sql b/fixtures/db_views/view_coderdojochi_session.sql new file mode 100644 index 00000000..9905dbe6 --- /dev/null +++ b/fixtures/db_views/view_coderdojochi_session.sql @@ -0,0 +1,26 @@ +SELECT + ses.id, + ses.course_id, + c.code, + c.title, + ses.start_date, + ses.old_end_date AS end_date, + l.name, + l.address, + l.zip, + ses.capacity, + ses.instructor_id AS teacher_id, + ses.is_active, + ses.created_at, + ses.mentor_capacity, + ses.gender_limitation +FROM + ( + ( + coderdojochi_session ses + LEFT JOIN coderdojochi_course c ON ((ses.course_id = c.id)) + ) + LEFT JOIN coderdojochi_location l ON ((ses.location_id = l.id)) + ) +WHERE + (ses.is_active = true); diff --git a/fixtures/db_views/view_coderdojochi_session_w_signups.sql b/fixtures/db_views/view_coderdojochi_session_w_signups.sql new file mode 100644 index 00000000..2f3ed3c3 --- /dev/null +++ b/fixtures/db_views/view_coderdojochi_session_w_signups.sql @@ -0,0 +1,62 @@ +SELECT + s.id, + s.course_id, + s.code, + s.title, + s.start_date, + s.end_date, + s.name, + s.address, + s.zip, + s.capacity, + s.teacher_id, + s.is_active, + s.created_at, + s.mentor_capacity, + s.gender_limitation, + COALESCE(so.student_signup, (0) :: bigint) AS student_signup, + COALESCE(so.student_attend, (0) :: bigint) AS student_attend, + COALESCE(mo.mentor_signup, (0) :: bigint) AS mentor_signup, + COALESCE(mo.mentor_attend, (0) :: bigint) AS mentor_attend +FROM + ( + ( + view_coderdojochi_session s + LEFT JOIN ( + SELECT + coderdojochi_order.session_id, + count(coderdojochi_order.student_id) AS student_signup, + sum( + CASE + WHEN (coderdojochi_order.check_in IS NOT NULL) THEN 1 + ELSE 0 + END + ) AS student_attend + FROM + coderdojochi_order + GROUP BY + coderdojochi_order.session_id + ORDER BY + coderdojochi_order.session_id + ) so ON ((s.id = so.session_id)) + ) + LEFT JOIN ( + SELECT + coderdojochi_mentororder.session_id, + count(coderdojochi_mentororder.mentor_id) AS mentor_signup, + sum( + CASE + WHEN (coderdojochi_mentororder.check_in IS NOT NULL) THEN 1 + ELSE 0 + END + ) AS mentor_attend + FROM + coderdojochi_mentororder + GROUP BY + coderdojochi_mentororder.session_id + ORDER BY + coderdojochi_mentororder.session_id + ) mo ON ((s.id = mo.session_id)) + ) +ORDER BY + s.start_date; diff --git a/fixtures/db_views/view_coderdojochi_student.sql b/fixtures/db_views/view_coderdojochi_student.sql new file mode 100644 index 00000000..fc814fcf --- /dev/null +++ b/fixtures/db_views/view_coderdojochi_student.sql @@ -0,0 +1,55 @@ +SELECT + max(s.id) AS id, + s.guardian_id, + g.zip AS guardian_zip, + s.first_name, + s.last_name, + s.birthday, + ( + date_part( + 'day' :: text, + ( + (CURRENT_DATE) :: timestamp with time zone - s.birthday + ) + ) / (365) :: double precision + ) AS age, + COALESCE(dq.mapping, 'unknown' :: character varying) AS gender, + max(s.created_at) AS created_at, + max(s.updated_at) AS updated_at, + s.is_active, + s.school_name, + s.school_type +FROM + ( + ( + coderdojochi_student s + LEFT JOIN ( + SELECT + coderdojochi_guardian.id, + coderdojochi_guardian.user_id, + coderdojochi_guardian.is_active, + coderdojochi_guardian.phone, + coderdojochi_guardian.created_at, + coderdojochi_guardian.updated_at, + coderdojochi_guardian.zip + FROM + coderdojochi_guardian + WHERE + (coderdojochi_guardian.is_active = true) + ) g ON ((s.guardian_id = g.id)) + ) + LEFT JOIN dq_lookup_gender dq ON (((dq.value) :: text = (s.gender) :: text)) + ) +WHERE + (s.is_active = true) +GROUP BY + s.guardian_id, + g.zip, + s.first_name, + s.last_name, + s.birthday, + s.gender, + s.is_active, + s.school_name, + s.school_type, + dq.mapping; diff --git a/fixtures/db_views/view_coderdojochi_student_w_eth.sql b/fixtures/db_views/view_coderdojochi_student_w_eth.sql new file mode 100644 index 00000000..3a8994aa --- /dev/null +++ b/fixtures/db_views/view_coderdojochi_student_w_eth.sql @@ -0,0 +1,33 @@ +SELECT + s.id, + s.guardian_id, + s.guardian_zip, + s.first_name, + s.last_name, + s.birthday, + s.age, + s.gender, + s.created_at, + s.updated_at, + s.is_active, + s.school_name, + s.school_type, + CASE + WHEN (sr.student_id IS NULL) THEN 'false' :: text + ELSE 'true' :: text + END AS ethnicity_entered, + COALESCE(sr.eth_american_indian, (0) :: bigint) AS eth_american_indian, + COALESCE(sr.eth_asian, (0) :: bigint) AS eth_asian, + COALESCE(sr.eth_arab, (0) :: bigint) AS eth_arab, + COALESCE(sr.eth_black_african_american, (0) :: bigint) AS eth_black_african_american, + COALESCE(sr.eth_hispanic_latino, (0) :: bigint) AS eth_hispanic_latino, + COALESCE(sr.eth_pacific_islander, (0) :: bigint) AS eth_pacific_islander, + COALESCE(sr.eth_white, (0) :: bigint) AS eth_white, + COALESCE(sr.eth_not_list, (0) :: bigint) AS eth_not_list +FROM + ( + view_coderdojochi_student s + LEFT JOIN view_student_race_ethnicity_unpivot sr ON ((s.id = sr.student_id)) + ) +ORDER BY + s.id; diff --git a/fixtures/db_views/view_school_names.sql b/fixtures/db_views/view_school_names.sql new file mode 100644 index 00000000..dfddf48d --- /dev/null +++ b/fixtures/db_views/view_school_names.sql @@ -0,0 +1,9 @@ +SELECT + DISTINCT coderdojochi_student.school_name, + count(*) AS count +FROM + coderdojochi_student +WHERE + (coderdojochi_student.is_active = true) +GROUP BY + coderdojochi_student.school_name; diff --git a/fixtures/db_views/view_student_race_ethnicity_unpivot.sql b/fixtures/db_views/view_student_race_ethnicity_unpivot.sql new file mode 100644 index 00000000..6517008b --- /dev/null +++ b/fixtures/db_views/view_student_race_ethnicity_unpivot.sql @@ -0,0 +1,57 @@ +SELECT + sr.student_id, + sum( + CASE + WHEN (r.id = 1) THEN 1 + ELSE 0 + END + ) AS eth_american_indian, + sum( + CASE + WHEN (r.id = 2) THEN 1 + ELSE 0 + END + ) AS eth_asian, + sum( + CASE + WHEN (r.id = 3) THEN 1 + ELSE 0 + END + ) AS eth_arab, + sum( + CASE + WHEN (r.id = 4) THEN 1 + ELSE 0 + END + ) AS eth_black_african_american, + sum( + CASE + WHEN (r.id = 5) THEN 1 + ELSE 0 + END + ) AS eth_hispanic_latino, + sum( + CASE + WHEN (r.id = 6) THEN 1 + ELSE 0 + END + ) AS eth_pacific_islander, + sum( + CASE + WHEN (r.id = 7) THEN 1 + ELSE 0 + END + ) AS eth_white, + sum( + CASE + WHEN (r.id = 8) THEN 1 + ELSE 0 + END + ) AS eth_not_list +FROM + ( + coderdojochi_student_race_ethnicity sr + LEFT JOIN coderdojochi_raceethnicity r ON ((sr.raceethnicity_id = r.id)) + ) +GROUP BY + sr.student_id; diff --git a/fixtures/db_views/view_weallcode_mentor.sql b/fixtures/db_views/view_weallcode_mentor.sql new file mode 100644 index 00000000..fbe4f944 --- /dev/null +++ b/fixtures/db_views/view_weallcode_mentor.sql @@ -0,0 +1,92 @@ +SELECT + m.id AS mentor_id, + u.id AS user_id, + ( + ((u.first_name) :: text || ' ' :: text) || (u.last_name) :: text + ) AS mentor_name, + m.is_active, + m.created_at, + date_trunc('month' :: text, m.created_at) AS created_month, + m.updated_at, + CASE + WHEN (m.background_check IS TRUE) THEN 1 + ELSE 0 + END AS background_check, + max(s.start_date) AS last_class_date, + min(s.start_date) AS first_class_date, + sum( + CASE + WHEN (s.start_date IS NOT NULL) THEN 1 + ELSE 0 + END + ) AS num_classes, + COALESCE( + sum( + date_part('hour' :: text, (s.end_date - s.start_date)) + ), + (0) :: double precision + ) AS hours_volunteered, + date_part( + 'days' :: text, + ( + (CURRENT_DATE) :: timestamp with time zone - max(s.start_date) + ) + ) AS days_since_last_class, + CASE + WHEN ( + date_part( + 'days' :: text, + ( + (CURRENT_DATE) :: timestamp with time zone - max(s.start_date) + ) + ) <= (365) :: double precision + ) THEN 1 + ELSE 0 + END AS active_mentor, + CASE + WHEN ( + sum( + CASE + WHEN (s.start_date IS NOT NULL) THEN 1 + ELSE 0 + END + ) > 0 + ) THEN 1 + ELSE 0 + END AS mentored +FROM + ( + ( + ( + coderdojochi_mentor m + LEFT JOIN coderdojochi_cdcuser u ON ((m.user_id = u.id)) + ) + LEFT JOIN coderdojochi_mentororder mo ON ( + ( + (mo.mentor_id = m.id) + AND (mo.is_active = true) + ) + ) + ) + LEFT JOIN view_coderdojochi_session s ON ( + ( + (mo.session_id = s.id) + AND (s.is_active = true) + ) + ) + ) +WHERE + (m.is_active = true) +GROUP BY + m.id, + m.user_id, + u.id, + ( + ((u.first_name) :: text || ' ' :: text) || (u.last_name) :: text + ), + m.is_active, + m.created_at, + m.updated_at, + m.background_check +ORDER BY + m.id; From 2499784a0175941631b2e81f593afe48e4bfeb73 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Mon, 18 Jan 2021 18:09:27 -0600 Subject: [PATCH 55/57] fix migration from merge --- .../migrations/0037_auto_20201229_1510.py | 18 ------------------ ...0812_1246.py => 0037_auto_20210118_1808.py} | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 coderdojochi/migrations/0037_auto_20201229_1510.py rename coderdojochi/migrations/{0036_auto_20200812_1246.py => 0037_auto_20210118_1808.py} (93%) diff --git a/coderdojochi/migrations/0037_auto_20201229_1510.py b/coderdojochi/migrations/0037_auto_20201229_1510.py deleted file mode 100644 index f3445023..00000000 --- a/coderdojochi/migrations/0037_auto_20201229_1510.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.2 on 2020-12-29 21:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('coderdojochi', '0036_auto_20200812_1246'), - ] - - operations = [ - migrations.AlterField( - model_name='cdcuser', - name='first_name', - field=models.CharField(blank=True, max_length=150, verbose_name='first name'), - ), - ] diff --git a/coderdojochi/migrations/0036_auto_20200812_1246.py b/coderdojochi/migrations/0037_auto_20210118_1808.py similarity index 93% rename from coderdojochi/migrations/0036_auto_20200812_1246.py rename to coderdojochi/migrations/0037_auto_20210118_1808.py index f8cfcfc3..1d902a7a 100644 --- a/coderdojochi/migrations/0036_auto_20200812_1246.py +++ b/coderdojochi/migrations/0037_auto_20210118_1808.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.7 on 2020-08-12 17:46 +# Generated by Django 3.1.5 on 2021-01-19 00:08 from django.db import migrations @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('coderdojochi', '0035_auto_20200811_2046'), + ('coderdojochi', '0036_auto_20210115_1624'), ] operations = [ From e7e9a195ca2d9a8f705825591f2ead7c92552be1 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Wed, 20 Jan 2021 16:20:24 -0600 Subject: [PATCH 56/57] Add full_name to Student model --- accounts/templates/account/guardian/home.html | 188 +++++++++--------- coderdojochi/admin.py | 5 +- coderdojochi/models/order.py | 2 +- coderdojochi/models/student.py | 4 + coderdojochi/old_views.py | 2 +- .../templates/guardian/session_sign_up.html | 4 +- coderdojochi/templates/student_detail.html | 2 +- coderdojochi/views/sessions.py | 2 +- 8 files changed, 106 insertions(+), 103 deletions(-) diff --git a/accounts/templates/account/guardian/home.html b/accounts/templates/account/guardian/home.html index f38acf5e..b918af48 100644 --- a/accounts/templates/account/guardian/home.html +++ b/accounts/templates/account/guardian/home.html @@ -6,110 +6,110 @@ {% block contained_content %}
-

Your Profile

-

Joined on {{ guardian.user.date_joined|naturalday|capfirst }}

+

Your Profile

+

Joined on {{ guardian.user.date_joined|naturalday|capfirst }}

-
- {% csrf_token %} -
-
{% bootstrap_field user_form.first_name %}
-
{% bootstrap_field user_form.last_name %}
-
{% bootstrap_field form.phone %}
-
{% bootstrap_field form.zip %}
-
{% bootstrap_field form.birthday %}
-
{% bootstrap_field form.gender %}
-
{% bootstrap_field form.race_ethnicity %}
-
- {% buttons %} - - {% endbuttons %} - +
+ {% csrf_token %} +
+
{% bootstrap_field user_form.first_name %}
+
{% bootstrap_field user_form.last_name %}
+
{% bootstrap_field form.phone %}
+
{% bootstrap_field form.zip %}
+
{% bootstrap_field form.birthday %}
+
{% bootstrap_field form.gender %}
+
{% bootstrap_field form.race_ethnicity %}
+
+ {% buttons %} + + {% endbuttons %} +
-

Students

- {% if students %} -
- - - - - - - - - - - {% for student in students %} - - - - - - - - {% endfor %} - -
Name
{{ student.first_name }} {{ student.last_name }}Edit Info
-

Add another student

- {% else %} -

You have no students registered yet, register a student now.

- {% endif %} +

Students

+ {% if students %} + + + + + + + + + + + + {% for student in students %} + + + + + + + + {% endfor %} + +
Name
{{ student.full_name }}Edit Info
+

Add another student

+ {% else %} +

You have no students registered yet, register a student now.

+ {% endif %}
-

Upcoming Classes

+

Upcoming Classes

- {% if upcoming_orders %} - - - - - - - - - - - {% for order in upcoming_orders %} - - - - - - - {% endfor %} - -
DateStudent
{{ order.session.start_date|date }} @ {{ order.session.start_date|time }}{{ order.student.first_name }} {{ order.student.last_name }}{% student_register_link order.student order.session %}
- {% else %} -

You have no upcoming classes.

- Find one now! - {% endif %} + {% if upcoming_orders %} + + + + + + + + + + + {% for order in upcoming_orders %} + + + + + + + {% endfor %} + +
DateStudent
{{ order.session.start_date|date }} @ {{ order.session.start_date|time }}{{ order.student.full_name }}{% student_register_link order.student order.session %}
+ {% else %} +

You have no upcoming classes.

+ Find one now! + {% endif %}
-

Past Classes

- {% if past_orders %} - - - - - - - - - - {% for order in past_orders %} - - - - - - {% endfor %} - -
ClassDateStudent
{% if order.session.course.code %}({{ order.session.course.code }}) {% endif %}{{ order.session.course.title }}{{ order.session.start_date|date }} @ {{ order.session.start_date|time }}{{ order.student.first_name }} {{ order.student.last_name }}
- {% else %} -

You have not participated in any classes yet. Find one now!

- {% endif %} +

Past Classes

+ {% if past_orders %} + + + + + + + + + + {% for order in past_orders %} + + + + + + {% endfor %} + +
ClassDateStudent
{% if order.session.course.code %}({{ order.session.course.code }}) {% endif %}{{ order.session.course.title }}{{ order.session.start_date|date }} @ {{ order.session.start_date|time }}{{ order.student.full_name }}
+ {% else %} +

You have not participated in any classes yet. Find one now!

+ {% endif %}
{% endblock %} diff --git a/coderdojochi/admin.py b/coderdojochi/admin.py index ea0cccae..3ea8de17 100644 --- a/coderdojochi/admin.py +++ b/coderdojochi/admin.py @@ -452,10 +452,9 @@ def get_queryset(self, request): def guardian_link(self, obj): return format_html( - '{first} {last}', + '{name}', url=reverse("admin:coderdojochi_guardian_change", args=(obj.guardian.id,)), - first=obj.guardian.first_name, - last=obj.guardian.last_name, + name=obj.guardian.full_name, ) guardian_link.short_description = "Guardian" diff --git a/coderdojochi/models/order.py b/coderdojochi/models/order.py index fd54107f..86c9a2c6 100644 --- a/coderdojochi/models/order.py +++ b/coderdojochi/models/order.py @@ -55,7 +55,7 @@ class Order(CommonInfo): ) def __str__(self): - return f"{self.student.first_name} {self.student.last_name} | {self.session.course.title}" + return f"{self.student.full_name} | {self.session.course.title}" def is_checked_in(self): return self.check_in is not None diff --git a/coderdojochi/models/student.py b/coderdojochi/models/student.py index f6e3e600..88f6643f 100644 --- a/coderdojochi/models/student.py +++ b/coderdojochi/models/student.py @@ -62,6 +62,10 @@ class Student(CommonInfo): def __str__(self): return f"{self.first_name} {self.last_name}" + @property + def full_name(self): + return f"{self.first_name} {self.last_name}" + def is_registered_for_session(self, session): from .order import Order diff --git a/coderdojochi/old_views.py b/coderdojochi/old_views.py index 90924611..80695a5c 100644 --- a/coderdojochi/old_views.py +++ b/coderdojochi/old_views.py @@ -164,7 +164,7 @@ def student_detail(request, student_id=False, template_name="student_detail.html if "delete" in request.POST: student.is_active = False student.save() - messages.success(request, f'Student "{student.first_name} {student.last_name}" Deleted.') + messages.success(request, f'Student "{student.full_name}" Deleted.') return redirect("account_home") form = StudentForm(request.POST, instance=student) diff --git a/coderdojochi/templates/guardian/session_sign_up.html b/coderdojochi/templates/guardian/session_sign_up.html index 108cf888..b0718727 100644 --- a/coderdojochi/templates/guardian/session_sign_up.html +++ b/coderdojochi/templates/guardian/session_sign_up.html @@ -10,7 +10,7 @@
{% if user_signed_up %} -

{{ student.first_name }} {{ student.last_name }} can no longer make it to the "{{ session.course.title }}" class on {{ session.start_date|date }} from {{ session.start_date|time }} to {{ session.end_date|time }}.

+

{{ student.full_name }} can no longer make it to the "{{ session.course.title }}" class on {{ session.start_date|date }} from {{ session.start_date|time }} to {{ session.end_date|time }}.

{% csrf_token %} @@ -20,7 +20,7 @@

{{ student.first_name }} {{ student.last_name }} {% else %} -

Enroll {{ student.first_name }} {{ student.last_name }} for the "{{ session.course.title }}" class on {{ session.start_date|date }} from {{ session.start_date|time }} to {{ session.end_date|time }}.

+

Enroll {{ student.full_name }} for the "{{ session.course.title }}" class on {{ session.start_date|date }} from {{ session.start_date|time }} to {{ session.end_date|time }}.

{% csrf_token %} diff --git a/coderdojochi/templates/student_detail.html b/coderdojochi/templates/student_detail.html index bdfb4fc6..b94b2b8f 100644 --- a/coderdojochi/templates/student_detail.html +++ b/coderdojochi/templates/student_detail.html @@ -2,7 +2,7 @@ {% load i18n bootstrap3 static %} -{% block title %}{{ student.first_name }} {{ student.last_name }} | {{ block.super }}{% endblock %} +{% block title %}{{ student.full_name }} | {{ block.super }}{% endblock %} {% block body_class %}page-student-detail{% endblock %} diff --git a/coderdojochi/views/sessions.py b/coderdojochi/views/sessions.py index cb51d180..12c6745f 100644 --- a/coderdojochi/views/sessions.py +++ b/coderdojochi/views/sessions.py @@ -93,7 +93,7 @@ def session_confirm_guardian(request, session_obj, order, student): } email( - subject=f"Upcoming class confirmation for {student.first_name} {student.last_name}", + subject=f"Upcoming class confirmation for {student.full_name}", template_name="class_confirm_guardian", merge_global_data=merge_global_data, recipients=[request.user.email], From 1c16b9f0258e42743df91d7cc826a9802af77e94 Mon Sep 17 00:00:00 2001 From: Ali Karbassi Date: Wed, 20 Jan 2021 16:47:59 -0600 Subject: [PATCH 57/57] Remove username from email confirmation page --- accounts/templates/account/email_confirm.html | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/accounts/templates/account/email_confirm.html b/accounts/templates/account/email_confirm.html index 5f0f5118..eed81df9 100644 --- a/accounts/templates/account/email_confirm.html +++ b/accounts/templates/account/email_confirm.html @@ -11,13 +11,11 @@

{% trans "Confirm E-mail Address" %}

{% if confirmation %} -{% user_display confirmation.email_address.user as user_display %} - -

{% blocktrans with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktrans %}

+

{% blocktrans with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for your account.{% endblocktrans %}

- {% csrf_token %} - + {% csrf_token %} + {% else %}