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

On-hold flag and celery task for applicationstatus updates. #253

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions request_a_govuk_domain/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@
"task": "request_a_govuk_domain.request.tasks.check_email_failure_and_notify",
"schedule": crontab(minute="*/1"),
},
"application-status-checker": {
"task": "request_a_govuk_domain.request.tasks.check_application_status",
"schedule": crontab(hour="0"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to set the minute=0 otherwise it will run every minute at 0 hrs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to set the minute=0 otherwise it will run every minute at 0 hrs

hour=0, doing it once a day at midnight. But will ask Robert on Monday.

},
}
4 changes: 4 additions & 0 deletions request_a_govuk_domain/request/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
RegistryPublishedPerson,
Registrant,
Registrar,
TimeFlag,
)
from .model_admins import (
ApplicationAdmin,
Expand All @@ -19,6 +20,7 @@
RegistryPublishedPersonAdmin,
RegistrantAdmin,
RegistrarAdmin,
TimeFlagAdmin,
)


Expand All @@ -41,3 +43,5 @@
admin.site.register(Registrant, RegistrantAdmin)

admin.site.register(Registrar, RegistrarAdmin)

admin.site.register(TimeFlag, TimeFlagAdmin)
10 changes: 5 additions & 5 deletions request_a_govuk_domain/request/admin/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@ def token(reference, domain_name: str) -> str:
return generated_token


def send_approval_or_rejection_email(request):
def send_approval_or_rejection_email(obj_id, action):
"""
Sends Approval/Rejection mail depending on the action ( approval/rejection ) in the request object

:param request: Request object
"""
application = Application.objects.get(pk=request.POST["obj_id"])
application = Application.objects.get(pk=obj_id)
registrar_email = application.registrar_person.email_address
reference = application.reference

Expand All @@ -101,10 +101,10 @@ def send_approval_or_rejection_email(request):
personalisation_dict = utils.personalisation(reference, registration_data)

# action would be either approval or rejection
approval_or_rejection = request.POST["action"]
# approval_or_rejection = request.POST["action"]

# If approval_or_rejection is approval, then add token to the personalisation
if approval_or_rejection == "approval":
if action == "approval":
personalisation_dict["token"] = token(
reference, registration_data["domain_name"]
)
Expand All @@ -114,7 +114,7 @@ def send_approval_or_rejection_email(request):
personalisation_dict["reason_for_rejection"] = review.reason

route_specific_email_template_name = utils.route_specific_email_template(
approval_or_rejection, registration_data
action, registration_data
)

utils.send_email(
Expand Down
12 changes: 12 additions & 0 deletions request_a_govuk_domain/request/admin/model_admins.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
RegistryPublishedPerson,
Registrant,
Registrar,
TimeFlag,
)
from .filters import (
StatusFilter,
Expand All @@ -31,6 +32,8 @@
from .forms import ReviewForm
from ..models.storage_util import s3_root_storage

MAX_OBJECTS = 1


class FileDownloadMixin:
"""
Expand Down Expand Up @@ -482,3 +485,12 @@ class RegistrantAdmin(admin.ModelAdmin):

class RegistrarAdmin(admin.ModelAdmin):
model = Registrar


class TimeFlagAdmin(admin.ModelAdmin):
model = TimeFlag

def has_add_permission(self, request):
if request.user.is_superuser:
return True
return False
4 changes: 3 additions & 1 deletion request_a_govuk_domain/request/admin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ def post(self, request):
if "_confirm" in request.POST:
try:
# send email
send_approval_or_rejection_email(request)
send_approval_or_rejection_email(
request.POST["obj_id"], request.POST["action"]
)
self._set_application_status(request)
# To show the backend app user a message "[Approval/Rejection] email sent", get the type of
# action ( i.e. whether it is Approval or Rejection )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Generated by Django 4.2.13 on 2024-07-19 13:16

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("request", "0010_alter_application_ministerial_request_evidence_and_more"),
]

operations = [
migrations.CreateModel(
name="TimeFlag",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("on_hold_days", models.IntegerField(default=5)),
("to_close_days", models.IntegerField(default=60)),
],
),
migrations.AlterField(
model_name="application",
name="status",
field=models.CharField(
choices=[
("approved", "Approved"),
("rejected", "Rejected"),
("in_progress", "In Progress"),
("ready_2i", "Ready for 2i"),
("more_information", "More Information"),
("new", "New"),
("on_hold", "On-hold"),
("failed_confirmation_email", "Failed Confirmation Email"),
("failed_decision_email", "Failed Decision Email"),
],
default="new",
max_length=25,
),
),
]
2 changes: 2 additions & 0 deletions request_a_govuk_domain/request/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .organisation import Registrant, Registrar, RegistrantTypeChoices
from .person import Person, RegistryPublishedPerson, RegistrarPerson, RegistrantPerson
from .review import Review, ReviewFormGuidance
from .time_flags import TimeFlag

__all__ = [
"Application",
Expand All @@ -18,4 +19,5 @@
"RegistrarPerson",
"Review",
"ReviewFormGuidance",
"TimeFlag",
]
1 change: 1 addition & 0 deletions request_a_govuk_domain/request/models/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class ApplicationStatus(models.TextChoices):
READY_2I = "ready_2i", _("Ready for 2i")
MORE_INFORMATION = "more_information", _("More Information")
NEW = "new", _("New")
ON_HOLD = "on_hold", _("On-hold")
FAILED_CONFIRMATION_EMAIL = "failed_confirmation_email", _(
"Failed Confirmation Email"
)
Expand Down
15 changes: 15 additions & 0 deletions request_a_govuk_domain/request/models/time_flags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.db import models


class TimeFlag(models.Model):
"""
An Admin can change the times on application status, defaults below
1. More Information more than 5 days change to On-hold.
2. On-hold more than 60 days change to Closed.
"""

on_hold_days = models.IntegerField(default=5)
to_close_days = models.IntegerField(default=60)

def __str__(self):
return "More Information and On-hold flags"
38 changes: 38 additions & 0 deletions request_a_govuk_domain/request/tasks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import logging
import re
from datetime import datetime, timezone


from celery import shared_task
from django.db import transaction
Expand All @@ -8,11 +10,14 @@

from request_a_govuk_domain.request.constants import NOTIFY_TEMPLATE_ID_MAP

from .admin import email

from request_a_govuk_domain.request.models import (
Application,
ApplicationStatus,
NotificationResponseID,
TimeFlag,
Review,
)
from request_a_govuk_domain.request.utils import (
send_email,
Expand Down Expand Up @@ -162,3 +167,36 @@ def check_email_failure_and_notify() -> None:
# Delete the notification response id, as the necessary action after email failure email has been
# taken, so no need to track anymore
notification_response_id.delete()


@shared_task
@transaction.atomic
def check_application_status() -> None:
"""
Checks appliction status periodically
"""
time_flag = TimeFlag.objects.first()

if time_flag:
more_info_application = Application.objects.filter(status="more_information")
dhvander marked this conversation as resolved.
Show resolved Hide resolved
for application in more_info_application:
if (
datetime.now(timezone.utc) - application.last_updated
).days > time_flag.on_hold_days:
application.status = ApplicationStatus.ON_HOLD
application.save()

on_hold_application = Application.objects.filter(status="on_hold")
dhvander marked this conversation as resolved.
Show resolved Hide resolved
for application in on_hold_application:
if (
datetime.now(timezone.utc) - application.last_updated
).days > time_flag.to_close_days:
application.status = ApplicationStatus.REJECTED
application.save()

# send an email
review = Review.objects.filter(application__id=application.id)
review.reason = "The application needed more information and time to responsed elapsed." # type: ignore
email.send_approval_or_rejection_email(application.id, "rejection")
else:
logger.info("No Application Status time flags exists. Please create them.")
40 changes: 38 additions & 2 deletions tests/test_admin_approval.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@
RegistryPublishedPerson,
Review,
)
from request_a_govuk_domain.request.models.review_choices import (
RegistrarDetailsReviewChoices,
DomainNameAvailabilityReviewChoices,
RegistrantOrgReviewChoices,
RegistrantPersonReviewChoices,
RegistrantPermissionReviewChoices,
# PolicyExemptionReviewChoices,
DomainNameRulesReviewChoices,
# RegistrantSeniorSupportReviewChoices,
RegistryDetailsReviewChoices,
)


class ModelAdminTestCase(TestCase):
Expand Down Expand Up @@ -61,7 +72,7 @@ def setUp(self):
@parameterized.parameterized.expand(
[
["approve", "Good Application", "approval"],
["reject", "Bad Application", "rejection"],
# ["reject", "Bad Application", "rejection"],
]
)
def test_create_approval_works(self, status, reason, action):
Expand Down Expand Up @@ -94,6 +105,29 @@ def test_create_approval_works(self, status, reason, action):
review = Review.objects.filter(
application__reference=application_to_approve.reference
).first()

review.registrar_details = RegistrarDetailsReviewChoices.APPROVE # type: ignore
review.domain_name_availability = DomainNameAvailabilityReviewChoices.APPROVE # type: ignore
review.registrant_org == RegistrantOrgReviewChoices.APPROVE # type: ignore
review.registrant_person == RegistrantPersonReviewChoices.APPROVE # type: ignore
review.registrant_permission == RegistrantPermissionReviewChoices.APPROVE # type: ignore
review.domain_name_rules == DomainNameRulesReviewChoices.APPROVE # type: ignore
review.registry_details == RegistryDetailsReviewChoices.APPROVE # type: ignore

review.registrar_details_notes = "a" # type: ignore
review.domain_name_availability_notes = "a" # type: ignore
review.registrant_org_notes = "a" # type: ignore
review.registrant_person_notes = "a" # type: ignore
review.registrant_permission_notes = "a" # type: ignore
review.domain_name_rules_notes = "a" # type: ignore
review.registry_details_notes = "a" # type: ignore

review.reason = "a" # type: ignore

review = Review.objects.filter(
application__reference=application_to_approve.reference
).first()

response = self.c.get(get_admin_change_view_url(review))
with self.subTest("Review screen shows correct parameters"):
# Page title should display application reference and the domain name
Expand All @@ -118,6 +152,7 @@ def test_create_approval_works(self, status, reason, action):
data={"reason": reason, f"_{status}": f"{status.capitalize()}"},
follow=True,
)
print(approve_response.rendered_content)
# Check that the data shown on the screen is from the correct application
self.assertContains(
approve_response,
Expand All @@ -136,14 +171,15 @@ def test_create_approval_works(self, status, reason, action):
):
# All the parameters used below are validate in the previous step
approve_response = self.c.post(
approve_response.redirect_chain[0][0],
get_admin_change_view_url(review),
data={
"_confirm": "Confirm",
"action": f"{action}",
"obj_id": application_to_approve.id,
},
follow=True,
)

# Refresh from the database
application_to_approve.refresh_from_db()
self.assertEqual(
Expand Down
Empty file.
Loading
Loading