Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Keep track of deleted unverified users #2174

Merged
merged 6 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 36 additions & 36 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions cfl_common/common/migrations/0044_update_activity_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 3.2.20 on 2023-09-12 11:58

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('common', '0043_add_total_activity'),
]

operations = [
migrations.AddField(
model_name='dailyactivity',
name='deleted_unverified_independents',
field=models.PositiveIntegerField(default=0),
),
migrations.AddField(
model_name='dailyactivity',
name='deleted_unverified_teachers',
field=models.PositiveIntegerField(default=0),
),
migrations.AddField(
model_name='totalactivity',
name='deleted_unverified_independents',
field=models.PositiveIntegerField(default=0),
),
migrations.AddField(
model_name='totalactivity',
name='deleted_unverified_teachers',
field=models.PositiveIntegerField(default=0),
),
]
8 changes: 6 additions & 2 deletions cfl_common/common/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import pgeocode
import re
from datetime import timedelta
from uuid import uuid4

import pgeocode
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
Expand Down Expand Up @@ -345,12 +345,14 @@ class DailyActivity(models.Model):
teacher_lockout_resets = models.PositiveIntegerField(default=0)
indy_lockout_resets = models.PositiveIntegerField(default=0)
school_student_lockout_resets = models.PositiveIntegerField(default=0)
deleted_unverified_teachers = models.PositiveIntegerField(default=0)
deleted_unverified_independents = models.PositiveIntegerField(default=0)

class Meta:
verbose_name_plural = "Daily activities"

def __str__(self):
return f"Activity on {self.date}: CSV clicks: {self.csv_click_count}, login cards clicks: {self.login_cards_click_count}, primary pack downloads: {self.primary_coding_club_downloads}, python pack downloads: {self.python_coding_club_downloads}, level control submits: {self.level_control_submits}, teacher lockout resets: {self.teacher_lockout_resets}, indy lockout resets: {self.indy_lockout_resets}, school student lockout resets: {self.school_student_lockout_resets}"
return f"Activity on {self.date}: CSV clicks: {self.csv_click_count}, login cards clicks: {self.login_cards_click_count}, primary pack downloads: {self.primary_coding_club_downloads}, python pack downloads: {self.python_coding_club_downloads}, level control submits: {self.level_control_submits}, teacher lockout resets: {self.teacher_lockout_resets}, indy lockout resets: {self.indy_lockout_resets}, school student lockout resets: {self.school_student_lockout_resets}, unverified teachers deleted: {self.deleted_unverified_teachers}, unverified independents deleted: {self.deleted_unverified_independents}"


class TotalActivity(models.Model):
Expand All @@ -362,6 +364,8 @@ class TotalActivity(models.Model):
teacher_registrations = models.PositiveIntegerField(default=0)
student_registrations = models.PositiveIntegerField(default=0)
independent_registrations = models.PositiveIntegerField(default=0)
deleted_unverified_teachers = models.PositiveIntegerField(default=0)
deleted_unverified_independents = models.PositiveIntegerField(default=0)

class Meta:
verbose_name_plural = "Total activity"
Expand Down
2 changes: 1 addition & 1 deletion cfl_common/common/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,5 @@ def test_daily_activity_serializer(self):

assert (
str(daily_activity)
== f"Activity on {daily_activity.date}: CSV clicks: 0, login cards clicks: 0, primary pack downloads: 0, python pack downloads: 0, level control submits: 0, teacher lockout resets: 0, indy lockout resets: 0, school student lockout resets: 0"
== f"Activity on {daily_activity.date}: CSV clicks: 0, login cards clicks: 0, primary pack downloads: 0, python pack downloads: 0, level control submits: 0, teacher lockout resets: 0, indy lockout resets: 0, school student lockout resets: 0, unverified teachers deleted: 0, unverified independents deleted: 0"
)
34 changes: 30 additions & 4 deletions portal/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import csv
import io
import json
from datetime import timedelta, date
from datetime import timedelta, date, datetime
from unittest.mock import patch, Mock, ANY

import PyPDF2
import pytest
from aimmo.models import Game
from common.models import Teacher, UserSession, Student, Class, DailyActivity, School, UserProfile, TotalActivity
from common.helpers.emails import NOTIFICATION_EMAIL
from common.models import (
Teacher,
UserSession,
Student,
Class,
DailyActivity,
School,
UserProfile,
TotalActivity,
)
from common.tests.utils.classes import create_class_directly
from common.tests.utils.organisation import create_organisation_directly, join_teacher_to_organisation
from common.tests.utils.student import (
Expand All @@ -25,17 +35,16 @@
from game.tests.utils.level import create_save_level
from rest_framework.test import APIClient, APITestCase

from cfl_common.common.helpers.emails import NOTIFICATION_EMAIL
from deploy import captcha
from portal.templatetags.app_tags import is_logged_in_as_admin_teacher
from portal.views.api import anonymise
from portal.views.cron.user import USER_DELETE_UNVERIFIED_ACCOUNT_DAYS
from portal.views.teacher.teach import (
REMINDER_CARDS_PDF_ROWS,
REMINDER_CARDS_PDF_COLUMNS,
REMINDER_CARDS_PDF_WARNING_TEXT,
count_student_details_click,
)
from portal.views.cron.user import USER_DELETE_UNVERIFIED_ACCOUNT_DAYS


class TestTeacherViews(TestCase):
Expand Down Expand Up @@ -994,6 +1003,14 @@ def delete_unverified_users(
new_user=indy_user,
)

activity_today = DailyActivity.objects.get_or_create(date=datetime.now().date())[0]
daily_teacher_count = activity_today.deleted_unverified_teachers
daily_indy_count = activity_today.deleted_unverified_independents

total_activity = TotalActivity.objects.get(id=1)
total_teacher_count = total_activity.deleted_unverified_teachers
total_indy_count = total_activity.deleted_unverified_independents

self.client.get(reverse("delete-unverified-accounts"))

# Assert the verified users and teach
Expand All @@ -1009,10 +1026,19 @@ def delete_unverified_users(
assert indy_user_exists == assert_exists
assert student_user_exists

activity_today = DailyActivity.objects.get_or_create(date=datetime.now().date())[0]
total_activity = TotalActivity.objects.get(id=1)

if teacher_user_exists:
teacher_user.delete()
else:
assert activity_today.deleted_unverified_teachers == daily_teacher_count + 1
assert total_activity.deleted_unverified_teachers == total_teacher_count + 1
if indy_user_exists:
indy_user.delete()
else:
assert activity_today.deleted_unverified_independents == daily_teacher_count + 1
assert total_activity.deleted_unverified_independents == total_indy_count + 1
if student_user_exists:
student_user.delete()

Expand Down
39 changes: 27 additions & 12 deletions portal/views/cron/user.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import logging
from datetime import timedelta
from datetime import timedelta, datetime

from common.helpers.emails import (
NOTIFICATION_EMAIL,
generate_token_for_email,
send_email,
)
from common.models import DailyActivity, TotalActivity
from django.contrib.auth.models import User
from django.db.models import F
from django.db.models.query import QuerySet
from django.urls import reverse
from django.utils import timezone
from rest_framework.response import Response
from rest_framework.views import APIView

from cfl_common.common.helpers.emails import (
NOTIFICATION_EMAIL,
generate_token_for_email,
send_email,
)

from ...mixins import CronMixin

# TODO: move email templates to DotDigital.
Expand All @@ -38,7 +39,7 @@
USER_DELETE_UNVERIFIED_ACCOUNT_DAYS = 19


def get_unverified_users(days: int, same_day: bool) -> QuerySet[User]:
def get_unverified_users(days: int, same_day: bool) -> (int, int, QuerySet[User]):
now = timezone.now()

# All expired unverified users.
Expand All @@ -58,7 +59,11 @@ def get_unverified_users(days: int, same_day: bool) -> QuerySet[User]:
new_student__class_field__isnull=True,
)

return teacher_queryset.union(independent_student_queryset)
return (
teacher_queryset.count(),
independent_student_queryset.count(),
teacher_queryset.union(independent_student_queryset),
)


def build_absolute_google_uri(request, location: str) -> str:
Expand All @@ -75,7 +80,7 @@ def build_absolute_google_uri(request, location: str) -> str:

class FirstVerifyEmailReminderView(CronMixin, APIView):
def get(self, request):
user_queryset = get_unverified_users(
_, _, user_queryset = get_unverified_users(
USER_1ST_VERIFY_EMAIL_REMINDER_DAYS,
same_day=True,
)
Expand Down Expand Up @@ -122,7 +127,7 @@ def get(self, request):

class SecondVerifyEmailReminderView(CronMixin, APIView):
def get(self, request):
user_queryset = get_unverified_users(
_, _, user_queryset = get_unverified_users(
USER_2ND_VERIFY_EMAIL_REMINDER_DAYS,
same_day=True,
)
Expand Down Expand Up @@ -171,7 +176,7 @@ class DeleteUnverifiedAccounts(CronMixin, APIView):
def get(self, request):
user_count = User.objects.count()

user_queryset = get_unverified_users(
teacher_count, indy_count, user_queryset = get_unverified_users(
USER_DELETE_UNVERIFIED_ACCOUNT_DAYS,
same_day=False,
)
Expand All @@ -186,4 +191,14 @@ def get(self, request):
user_count -= User.objects.count()
logging.info(f"{user_count} unverified users deleted.")

activity_today = DailyActivity.objects.get_or_create(date=datetime.now().date())[0]
activity_today.deleted_unverified_teachers = teacher_count
activity_today.deleted_unverified_independents = indy_count
activity_today.save()

TotalActivity.objects.update(
deleted_unverified_teachers=F("deleted_unverified_teachers") + teacher_count,
deleted_unverified_independents=F("deleted_unverified_independents") + indy_count,
)

return Response()