Browse Source

Code to send monthly reports (but no management command yet to actually send them)

pull/20/head
Pēteris Caune 9 years ago
parent
commit
91e6f80d9a
18 changed files with 353 additions and 13 deletions
  1. +19
    -0
      hc/accounts/admin.py
  2. +5
    -0
      hc/accounts/forms.py
  3. +0
    -0
      hc/accounts/management/__init__.py
  4. +0
    -0
      hc/accounts/management/commands/__init__.py
  5. +14
    -0
      hc/accounts/management/commands/createprofiles.py
  6. +24
    -0
      hc/accounts/migrations/0001_initial.py
  7. +45
    -1
      hc/accounts/models.py
  8. +13
    -4
      hc/accounts/urls.py
  9. +37
    -1
      hc/accounts/views.py
  10. +5
    -0
      hc/lib/emails.py
  11. +1
    -2
      static/css/base.css
  12. +12
    -0
      static/css/settings.css
  13. +44
    -0
      templates/accounts/profile.html
  14. +12
    -0
      templates/accounts/unsubscribed.html
  15. +14
    -5
      templates/base.html
  16. +4
    -0
      templates/emails/alert-body-html.html
  17. +102
    -0
      templates/emails/report-body-html.html
  18. +2
    -0
      templates/emails/report-subject.html

+ 19
- 0
hc/accounts/admin.py View File

@ -1,10 +1,22 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from hc.accounts.models import Profile
from hc.api.models import Channel, Check
@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
list_display = ("id", "email", "reports_allowed", "next_report_date")
search_fields = ["user__email"]
def email(self, obj):
return obj.user.email
class HcUserAdmin(UserAdmin):
actions = ["send_report"]
list_display = ('id', 'username', 'email', 'date_joined', 'involvement',
'is_staff')
@ -33,6 +45,13 @@ class HcUserAdmin(UserAdmin):
involvement.allow_tags = True
def send_report(self, request, qs):
for user in qs:
profile = Profile.objects.for_user(user)
profile.send_report()
self.message_user(request, "%d email(s) sent" % qs.count())
admin.site.unregister(User)
admin.site.register(User, HcUserAdmin)

+ 5
- 0
hc/accounts/forms.py View File

@ -2,6 +2,7 @@ from django import forms
class LowercaseEmailField(forms.EmailField):
def clean(self, value):
value = super(LowercaseEmailField, self).clean(value)
return value.lower()
@ -9,3 +10,7 @@ class LowercaseEmailField(forms.EmailField):
class EmailForm(forms.Form):
email = LowercaseEmailField()
class ReportSettingsForm(forms.Form):
reports_allowed = forms.BooleanField(required=False)

+ 0
- 0
hc/accounts/management/__init__.py View File


+ 0
- 0
hc/accounts/management/commands/__init__.py View File


+ 14
- 0
hc/accounts/management/commands/createprofiles.py View File

@ -0,0 +1,14 @@
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from hc.accounts.models import Profile
class Command(BaseCommand):
help = 'Make sure all users have profiles'
def handle(self, *args, **options):
for user in User.objects.all():
# this should create profile object if it does not exist
Profile.objects.for_user(user)
print("Done.")

+ 24
- 0
hc/accounts/migrations/0001_initial.py View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)),
('next_report_date', models.DateTimeField(null=True, blank=True)),
('reports_allowed', models.BooleanField(default=True)),
('user', models.OneToOneField(blank=True, to=settings.AUTH_USER_MODEL, null=True)),
],
),
]

+ 45
- 1
hc/accounts/models.py View File

@ -1,3 +1,47 @@
from datetime import timedelta
from django.conf import settings
from django.contrib.auth.models import User
from django.core import signing
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone
from hc.lib import emails
import uuid
# Create your models here.
class ProfileManager(models.Manager):
def for_user(self, user):
try:
profile = self.get(user_id=user.id)
except Profile.DoesNotExist:
profile = Profile(user=user)
profile.save()
return profile
class Profile(models.Model):
user = models.OneToOneField(User, blank=True, null=True)
next_report_date = models.DateTimeField(null=True, blank=True)
reports_allowed = models.BooleanField(default=True)
objects = ProfileManager()
def send_report(self):
# reset next report date first:
now = timezone.now()
self.next_report_date = now + timedelta(days=30)
self.save()
token = signing.Signer().sign(uuid.uuid4())
path = reverse("hc-unsubscribe-reports", args=[self.user.username])
unsub_link = "%s%s?token=%s" % (settings.SITE_ROOT, path, token)
ctx = {
"checks": self.user.check_set.order_by("created"),
"now": now,
"unsub_link": unsub_link
}
emails.report(self.user.email, ctx)

+ 13
- 4
hc/accounts/urls.py View File

@ -2,8 +2,17 @@ from django.conf.urls import url
from hc.accounts import views
urlpatterns = [
url(r'^login/$', views.login, name="hc-login"),
url(r'^logout/$', views.logout, name="hc-logout"),
url(r'^login_link_sent/$', views.login_link_sent, name="hc-login-link-sent"),
url(r'^check_token/([\w-]+)/([\w-]+)/$', views.check_token, name="hc-check-token"),
url(r'^login/$', views.login, name="hc-login"),
url(r'^logout/$', views.logout, name="hc-logout"),
url(r'^login_link_sent/$',
views.login_link_sent, name="hc-login-link-sent"),
url(r'^check_token/([\w-]+)/([\w-]+)/$',
views.check_token, name="hc-check-token"),
url(r'^profile/$', views.profile, name="hc-profile"),
url(r'^unsubscribe_reports/([\w-]+)/$',
views.unsubscribe_reports, name="hc-unsubscribe-reports"),
]

+ 37
- 1
hc/accounts/views.py View File

@ -1,14 +1,18 @@
import uuid
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth import authenticate
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core import signing
from django.core.urlresolvers import reverse
from django.http import HttpResponseBadRequest
from django.shortcuts import redirect, render
from hc.accounts.forms import EmailForm
from hc.accounts.forms import EmailForm, ReportSettingsForm
from hc.accounts.models import Profile
from hc.api.models import Channel, Check
from hc.lib import emails
@ -108,3 +112,35 @@ def check_token(request, username, token):
ctx = {"bad_link": True}
return render(request, "accounts/login.html", ctx)
@login_required
def profile(request):
profile = Profile.objects.for_user(request.user)
if request.method == "POST":
form = ReportSettingsForm(request.POST)
if form.is_valid():
profile.reports_allowed = form.cleaned_data["reports_allowed"]
profile.save()
messages.info(request, "Your settings have been updated!")
ctx = {
"profile": profile
}
return render(request, "accounts/profile.html", ctx)
def unsubscribe_reports(request, username):
try:
signing.Signer().unsign(request.GET.get("token"))
except signing.BadSignature:
return HttpResponseBadRequest()
user = User.objects.get(username=username)
profile = Profile.objects.for_user(user)
profile.reports_allowed = False
profile.save()
return render(request, "accounts/unsubscribed.html")

+ 5
- 0
hc/lib/emails.py View File

@ -14,3 +14,8 @@ def alert(to, ctx):
def verify_email(to, ctx):
o = InlineCSSTemplateMail("verify-email")
o.send(to, ctx)
def report(to, ctx):
o = InlineCSSTemplateMail("report")
o.send(to, ctx)

+ 1
- 2
static/css/base.css View File

@ -24,14 +24,13 @@ body {
margin-top: -16px;
}
.navbar-default .navbar-nav > li > a {
#nav-main-sections > li > a {
text-transform: uppercase;
font-size: small;
}
@media (min-width: 768px) {
.navbar-default .navbar-nav > li.active > a, .navbar-default .navbar-nav > li > a:hover {
background: none;
border-bottom: 5px solid #eee;
padding-bottom: 25px;
}


+ 12
- 0
static/css/settings.css View File

@ -0,0 +1,12 @@
#settings-title {
padding-bottom: 24px;
}
.settings-block {
padding: 24px;
}
.settings-block h2 {
margin: 0;
padding-bottom: 24px;
}

+ 44
- 0
templates/accounts/profile.html View File

@ -0,0 +1,44 @@
{% extends "base.html" %}
{% load compress staticfiles %}
{% block title %}Account Settings - healthchecks.io{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<h1 id="settings-title">Settings</h1>
</div>
{% if messages %}
<div class="col-sm-12">
{% for message in messages %}
<p class="alert alert-success">{{ message }}</p>
{% endfor %}
</div>
{% endif %}
</div>
<div class="row">
<div class="col-sm-6">
<div class="panel panel-default">
<div class="panel-body settings-block">
<form method="post">
{% csrf_token %}
<h2>Monthly Reports</h2>
<label>
<input
name="reports_allowed"
type="checkbox"
{% if profile.reports_allowed %} checked {% endif %}>
Each month send me a summary of my checks
</label>
<button
type="submit"
class="btn btn-default pull-right">Save</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

+ 12
- 0
templates/accounts/unsubscribed.html View File

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div class="hc-dialog">
<h1>You have been unsubscribed</h1>
</div>
</div>
</div>
{% endblock %}

+ 14
- 5
templates/base.html View File

@ -26,6 +26,7 @@
<link rel="stylesheet" href="{% static 'css/channel_checks.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/log.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/add_pushover.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/settings.css' %}" type="text/css">
{% endcompress %}
</head>
<body class="page-{{ page }}">
@ -68,7 +69,7 @@
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<ul id="nav-main-sections" class="nav navbar-nav">
{% if request.user.is_authenticated %}
<li {% if page == 'checks' %} class="active" {% endif %}>
<a href="{% url 'hc-checks' %}">Checks</a>
@ -105,10 +106,18 @@
{% endif %}
{% if request.user.is_authenticated %}
<p class="navbar-text navbar-right">
{{ request.user.email }}
<a href="{% url 'hc-logout' %}">Log Out</a>
</p>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a id="nav-email" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">
{{ request.user.email }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="{% url 'hc-profile' %}">Settings</a></li>
<li role="separator" class="divider"></li>
<li><a href="{% url 'hc-logout' %}">Log Out</a></li>
</ul>
</li>
</ul>
{% endif %}
</div>


+ 4
- 0
templates/emails/alert-body-html.html View File

@ -66,6 +66,10 @@
{% else %}
<span class="unnamed">unnamed</span>
{% endif %}
{% if check.tags %}
<br />
<small>{{ check.tags }}</small>
{% endif %}
</td>
<td class="url-cell">
<code>{{ check.url }}</code>


+ 102
- 0
templates/emails/report-body-html.html View File

@ -0,0 +1,102 @@
{% load humanize hc_extras %}
<style>
th {
text-align: left;
padding: 8px;
}
td {
border-top: 1px solid #ddd;
padding: 8px;
}
.badge {
font-size: 10px;
color: white;
padding: 4px;
font-family: sans;
}
.new { background: #AAA; }
.paused { background: #AAA; }
.up { background: #5cb85c; }
.grace { background: #f0ad4e; }
.down { background: #d9534f; }
.unnamed {
color: #888;
font-style: italic;
}
</style>
<p>Hello,</p>
<p>This is a monthly report sent by <a href="https://healthchecks.io">healthchecks.io</a>.</p>
<table>
<tr>
<th></th>
<th>Name</th>
<th>URL</th>
<th>Period</th>
<th>Last Ping</th>
</tr>
{% for check in checks %}
<tr>
<td>
{% if check.get_status == "new" %}
<span class="badge new">NEW</span>
{% elif check.get_status == "up" %}
<span class="badge up">UP</span>
{% elif check.get_status == "grace" %}
<span class="badge grace">LATE</span>
{% elif check.get_status == "down" %}
<span class="badge down">DOWN</span>
{% elif check.get_status == "paused" %}
<span class="badge paused">PAUSED</span>
{% endif %}
</td>
<td>
{% if check.name %}
{{ check.name }}
{% else %}
<span class="unnamed">unnamed</span>
{% endif %}
{% if check.tags %}
<br />
<small>{{ check.tags }}</small>
{% endif %}
</td>
<td class="url-cell">
<code>{{ check.url }}</code>
</td>
<td>
{{ check.timeout|hc_duration }}
</td>
<td>
{% if check.last_ping %}
{{ check.last_ping|naturaltime }}
{% else %}
Never
{% endif %}
</td>
</tr>
{% endfor %}
</table>
<p><strong>Just one more thing to check:</strong>
Do you have more cron jobs,
not yet on this list, that would benefit from monitoring?
Get the ball rolling by adding one more!</p>
<p>
--<br />
Regards,<br />
healthchecks.io
</p>
<p>
<a href="{{ unsub_link }}">Unsubscribe from future monthly reports</a>
</p>

+ 2
- 0
templates/emails/report-subject.html View File

@ -0,0 +1,2 @@
Monthly Report

Loading…
Cancel
Save