@ -0,0 +1,63 @@ | |||||
from datetime import timedelta | |||||
import time | |||||
from django.conf import settings | |||||
from django.core.management.base import BaseCommand | |||||
from django.utils.timezone import now | |||||
from hc.accounts.models import Profile, Member | |||||
from hc.api.models import Ping | |||||
from hc.lib import emails | |||||
class Command(BaseCommand): | |||||
help = """Send deletion notices to inactive user accounts. | |||||
Conditions for sending the notice: | |||||
- deletion notice has not been sent recently | |||||
- last login more than a year ago | |||||
- none of the owned projects has invited team members | |||||
""" | |||||
def handle(self, *args, **options): | |||||
year_ago = now() - timedelta(days=365) | |||||
q = Profile.objects.order_by("id") | |||||
# Exclude accounts with logins in the last year_ago | |||||
q = q.exclude(user__last_login__gt=year_ago) | |||||
# Exclude accounts less than a year_ago old | |||||
q = q.exclude(user__date_joined__gt=year_ago) | |||||
# Exclude accounts with the deletion notice already sent | |||||
q = q.exclude(deletion_notice_date__gt=year_ago) | |||||
# Exclude paid accounts | |||||
q = q.exclude(sms_limit__gt=0) | |||||
sent = 0 | |||||
for profile in q: | |||||
members = Member.objects.filter(project__owner_id=profile.user_id) | |||||
if members.exists(): | |||||
print("Skipping %s, has team members" % profile) | |||||
continue | |||||
pings = Ping.objects | |||||
pings = pings.filter(owner__project__owner_id=profile.user_id) | |||||
pings = pings.filter(created__gt=year_ago) | |||||
if pings.exists(): | |||||
print("Skipping %s, has pings in last year" % profile) | |||||
continue | |||||
self.stdout.write("Sending notice to %s" % profile.user.email) | |||||
profile.deletion_notice_date = now() | |||||
profile.save() | |||||
ctx = { | |||||
"email": profile.user.email, | |||||
"support_email": settings.SUPPORT_EMAIL | |||||
} | |||||
emails.deletion_notice(profile.user.email, ctx) | |||||
# Throttle so we don't send too many emails at once: | |||||
time.sleep(1) | |||||
sent += 1 | |||||
return "Done! Sent %d notices" % sent |
@ -0,0 +1,18 @@ | |||||
# Generated by Django 2.1.7 on 2019-03-12 17:16 | |||||
from django.db import migrations, models | |||||
class Migration(migrations.Migration): | |||||
dependencies = [ | |||||
('accounts', '0026_auto_20190204_2042'), | |||||
] | |||||
operations = [ | |||||
migrations.AddField( | |||||
model_name='profile', | |||||
name='deletion_notice_date', | |||||
field=models.DateTimeField(blank=True, null=True), | |||||
), | |||||
] |
@ -0,0 +1,18 @@ | |||||
# Generated by Django 2.1.7 on 2019-03-12 17:16 | |||||
from django.db import migrations, models | |||||
class Migration(migrations.Migration): | |||||
dependencies = [ | |||||
('api', '0057_auto_20190118_1319'), | |||||
] | |||||
operations = [ | |||||
migrations.AlterField( | |||||
model_name='channel', | |||||
name='kind', | |||||
field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('pagertree', 'PagerTree'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord'), ('telegram', 'Telegram'), ('sms', 'SMS'), ('zendesk', 'Zendesk'), ('trello', 'Trello'), ('matrix', 'Matrix')], max_length=20), | |||||
), | |||||
] |
@ -31,6 +31,7 @@ SECRET_KEY = os.getenv("SECRET_KEY", "---") | |||||
DEBUG = envbool("DEBUG", "True") | DEBUG = envbool("DEBUG", "True") | ||||
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "*").split(",") | ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "*").split(",") | ||||
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "[email protected]") | DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "[email protected]") | ||||
SUPPORT_EMAIL = os.getenv("SUPPORT_EMAIL") | |||||
USE_PAYMENTS = envbool("USE_PAYMENTS", "False") | USE_PAYMENTS = envbool("USE_PAYMENTS", "False") | ||||
REGISTRATION_OPEN = envbool("REGISTRATION_OPEN", "True") | REGISTRATION_OPEN = envbool("REGISTRATION_OPEN", "True") | ||||
@ -0,0 +1,20 @@ | |||||
{% extends "emails/base.html" %} | |||||
{% load hc_extras %} | |||||
{% block content %} | |||||
Hello,<br /> | |||||
We’re sending this email to notify you that your {% site_name %} account, registered to {{ email }} has been inactive for 1 year or more. If you no longer wish to keep your {% site_name %} account active then we will make sure that your account is closed and any data associated with your account is permanently deleted from our systems.<br /><br /> | |||||
If you wish to keep your account, simply log in within 30 days. If you continue to be inactive, <strong>your account will be permanently deleted after the 30 day period</strong>.<br /><br /> | |||||
If you have issues logging in, or have any questions, please reach out to us at {{ support_email }}.<br /><br /> | |||||
Sincerely,<br /> | |||||
The {% site_name %} Team | |||||
{% endblock %} | |||||
{% block unsub %} | |||||
<br /> | |||||
This is a one-time message we're sending out to notify you about your account closure. | |||||
{% endblock %} |
@ -0,0 +1,14 @@ | |||||
{% load hc_extras %} | |||||
Hello, | |||||
We’re sending this email to notify you that your {% site_name %} account, registered to {{ email }} has been inactive for 1 year or more. If you no longer wish to keep your {% site_name %} account active then we will make sure that your account is closed and any data associated with your account is permanently deleted from our systems. | |||||
If you wish to keep your account, simply log in within 30 days. If you continue to be inactive, your account will be permanently deleted after the 30 day period. | |||||
If you have issues logging in, or have any questions, please reach out to us at {{ support_email }}. | |||||
This is a one-time message we're sending out to notify you about your account closure. | |||||
-- | |||||
Sincerely, | |||||
The {% site_name %} Team |
@ -0,0 +1 @@ | |||||
Inactive Account Notification |