Skip to content
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
Empty file added newsletter/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions newsletter/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.contrib import admin

from newsletter.models import NewsletterCache


class NewsletterCacheAdmin(admin.ModelAdmin):
pass


admin.site.register(NewsletterCache, NewsletterCacheAdmin)
5 changes: 5 additions & 0 deletions newsletter/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class WeeklyNewsletterConfig(AppConfig):
name = 'newsletter'
Empty file.
Empty file.
10 changes: 10 additions & 0 deletions newsletter/management/commands/send_newsletter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.core.management.base import BaseCommand

from newsletter.services import Newsletter


class Command(BaseCommand):
help = 'tbd'

def handle(self, *args, **kwargs):
Newsletter()
27 changes: 27 additions & 0 deletions newsletter/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-19 03:26
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='NewsletterCache',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('last_time_sent', models.DateTimeField()),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
20 changes: 20 additions & 0 deletions newsletter/migrations/0002_auto_20180219_0326.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-19 03:26
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('newsletter', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='newslettercache',
name='last_time_sent',
field=models.DateTimeField(null=True),
),
]
20 changes: 20 additions & 0 deletions newsletter/migrations/0003_newslettercache_subscribes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-22 05:43
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('newsletter', '0002_auto_20180219_0326'),
]

operations = [
migrations.AddField(
model_name='newslettercache',
name='subscribes',
field=models.BooleanField(default=False),
),
]
Empty file.
8 changes: 8 additions & 0 deletions newsletter/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.contrib.auth.models import User
from django.db import models


class NewsletterCache(models.Model):
user = models.ForeignKey(User)
last_time_sent = models.DateTimeField(null=True)
subscribes = models.BooleanField(default=False)
80 changes: 80 additions & 0 deletions newsletter/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from datetime import datetime, timedelta

from django.conf import settings
from django.contrib.auth.models import User
from django.core.mail import EmailMultiAlternatives
from django.core.signing import Signer
from django.template.loader import get_template
from django.urls import reverse

from newsletter.models import NewsletterCache
from package.models import TimelineEvent, Project


class Newsletter:
NEWSLETTER_FREQUENCY_IN_DAYS = 7
AMOUNT_OF_LATEST_PROJECTS_IN_NEWSLETTER = 3

@staticmethod
def get_unsubscribe_link(user):
token = Signer().sign(user.username).split(':')[1]
return reverse('unsubscribe', kwargs={'username': user.username, 'token': token})

@staticmethod
def get_user_favorite_projects(user):
return user.project_set.all()

def __init__(self):
for user in User.objects.all():
newsletter_cache, _ = NewsletterCache.objects.get_or_create(user=user)
newsletter_cache.subscribes = True
if not newsletter_cache.subscribes:
continue
# newsletter_cache.last_time_sent = datetime.now()
newsletter_cache.save()
self.send_newsletter(user)

@staticmethod
def get_favorite_project_events(user):
newsletter_cache, _ = NewsletterCache.objects.get_or_create(user=user)
favorite_projects = Newsletter.get_user_favorite_projects(user)
timeline_events = TimelineEvent.objects.filter(
project__in=favorite_projects,
date__gte=datetime.now() - timedelta(days=Newsletter.NEWSLETTER_FREQUENCY_IN_DAYS))
return timeline_events

@staticmethod
def get_latest_projects():
return Project.objects.all().order_by('-id')[:Newsletter.AMOUNT_OF_LATEST_PROJECTS_IN_NEWSLETTER]

@staticmethod
def send_newsletter(user):
favorite_project_events = Newsletter.get_favorite_project_events(user)
if not favorite_project_events:
return False

latest_projects = Newsletter.get_latest_projects()

unsubscribe_link = Newsletter.get_unsubscribe_link(user)

plain_template = get_template('newsletter.txt')
html_template = get_template('newsletter.html')

d = {'username': user.username,
'favorite_project_events': favorite_project_events,
'latest_projects': latest_projects,
'unsubscribe_link': unsubscribe_link,
}

plain_content = plain_template.render(d)
html_content = html_template.render(d)

# msg = EmailMultiAlternatives(
# subject='{0} Newsletter'.format(settings.EMAIL_SUBJECT_PREFIX),
# body=plain_content,
# from_email=settings.VALIDATION_EMAIL_SENDER,
# to=['patryk@perduta.net'],
# )
# msg.attach_alternative(html_content, 'text/html')
# msg.esp_extra = {"sender_domain": settings.EMAIL_SENDER_DOMAIN}
# msg.send()
119 changes: 119 additions & 0 deletions newsletter/templates/newsletter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html>
<head>
<title>Happy email</title>
<meta charset="UTF-8">
</head>
<body>
<table cellpadding="0" cellspacing="0">
<tr>
<td class="pattern" width="600">
<table cellpadding="0" cellspacing="0">
<tr>
<td style="background-color: rgb(74, 161, 241); font-size: 60px;">
&nbsp;
</td>
</tr>
<tr>
<td align="center" style="">
<br/><br/>
<h1 style="margin:0 auto;margin-bottom: 8px;"><a href="https://steemprojects.com/"><img
src="logo-steemprojects.png" alt="Logo steemprojects"/></a></h1>
<h2 style="font-family: sans-serif; color: #888; font-size: 13px; letter-spacing: 3px; font-weight: 300;">
Latest events from your favorite projects
</h2>
<br/><br/><br/>
</td>
</tr>
<tr class="row">
<td class="col" valign="top" width="600" style="padding-bottom: 20px; padding-top: 20px;">
<table cellpadding="0" cellspacing="0">
<tr>
<td align="left" valign="top" style="padding-left: 10px;">
<img src="http://placehold.it/180x120/333333&text=Thumbnail" alt=""
style="display: block; border: 0;"/>
</td>
<td class="description" align="left" valign="middle"
style="font-family: Helvetica Neue, sans-serif;color: #333; padding: 0 18px;">
<strong style="font-size: 28px; line-height: 1.4; color: #444;">SteemConnect 2.0:
Easy, Fast, Efficient Access to the Steem Blockchain</strong>
</td>
</tr>
</table>
</td>
</tr>
<tr class="row">
<td class="col" valign="top" align="middle" width="600" style="">
<hr style="width: 500px; border: 1px solid rgb(74, 161, 241);"/>
</td>
</tr>
<tr class="row">
<td class="col" valign="top" width="600" style="padding-bottom: 20px; padding-top: 20px;">
<table cellpadding="0" cellspacing="0">
<tr>
<td align="left" valign="top" style="padding-left: 10px;">
<img src="http://placehold.it/180x120/333333&text=Thumbnail" alt=""
style="display: block; border: 0;"/>
</td>
<td class="description" align="left" valign="middle"
style="font-family: Helvetica Neue, sans-serif;color: #333; padding: 0 18px;">
<strong style="font-size: 28px; line-height: 1.4; color: #444;">
Introducing SteemConnect by Busy : Identity, authentication, authorization for
Steem blockchain’s ap
</strong>
</td>
</tr>
</table>
</td>
</tr>
<tr class="row">
<td class="col" valign="top" align="middle" width="600" style="">
<hr style="width: 500px; border: 1px solid rgb(74, 161, 241);"/>
</td>
</tr>
<tr class="row">
<td class="col" valign="top" width="600" style="padding-bottom: 20px; padding-top: 20px;">
<table cellpadding="0" cellspacing="0">
<tr>
<td align="left" valign="top" style="padding-left: 10px;">
<img src="http://placehold.it/180x120/333333&text=Thumbnail" alt=""
style="display: block; border: 0;"/>
</td>
<td class="description" align="left" valign="middle"
style="font-family: Helvetica Neue, sans-serif;color: #333; padding: 0 18px;">
<strong style="font-size: 28px; line-height: 1.4; color: #444;">
The REALLY gentle guide to becoming a witness
</strong>
</td>
</tr>
</table>
</td>
</tr>
<tr class="row">
<td class="col" valign="top" width="600"
style="background: rgb(74, 161, 241); color: white; font-family: sans-serif; padding: 40px; padding-top: 15px;">

<p align="left">
Your welcome, @steemprojects
<a style="text-decoration: underline; color: white; float:right;" href="">Follow us on
steem</a>
</p>
<br/>
<br/>
<br/>
</td>
</tr>
<tr class="row">
<td class="col" style="font-family: sans-serif;">
<p style="font-size: 12px;" align="center">
To unsubscribe click this link: <a href=""
style="text-decoration: underline;">unsubscribe</a>.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
Empty file.
6 changes: 6 additions & 0 deletions newsletter/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.test import TestCase


class NewsletterServiceTestCase(TestCase):
def test_get_user_favorite_projects(self):
self.fail()
8 changes: 8 additions & 0 deletions newsletter/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.conf.urls import url

from newsletter.views import unsubscribe, ask_for_newsletter

urlpatterns = [
url(regex=r'^unsubscribe/(?P<username>[\w.@+-]+)/(?P<token>[\w.:\-_=]+)/$', view=unsubscribe, name='unsubscribe'),
url(regex=r'^ask/$', view=ask_for_newsletter, name='ask_for_newsletter'),
]
42 changes: 42 additions & 0 deletions newsletter/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from django.contrib import messages
from django.contrib.auth.models import User
from django.core.signing import Signer, BadSignature
from django.shortcuts import render, redirect, get_object_or_404
from social_django.utils import load_strategy

from newsletter.models import NewsletterCache
from social_auth_local.decorators import render_to


def unsubscribe(request, username, token):
user = get_object_or_404(User, username=username)
newsletter_cache = NewsletterCache.objects.get(user=user)

try:
key = '{}:{}'.format(username, token)
Signer().unsign(key)
except BadSignature:
messages.add_message(request, messages.ERROR, 'Your subscribtion cancellation link is invalid.')
return redirect('/')

if newsletter_cache.subscribes:
newsletter_cache.subscribes = False
newsletter_cache.save()
messages.add_message(request, messages.INFO, 'You\'ve been succesfully unsubscribed from our newsletter. ;-(')
return redirect('/')

messages.add_message(request, messages.INFO, 'You are already have been unsubscribed from our newsletter!')
return redirect('/')


@render_to('social_auth_local/ask_for_newsletter.html')
def ask_for_newsletter(request):
strategy = load_strategy()
partial_token = request.GET.get('partial_token')
partial = strategy.partial_load(partial_token)

return {
'ask_for_newsletter': True,
'partial_backend_name': partial.backend if partial else None,
'partial_token': partial_token,
}
6 changes: 5 additions & 1 deletion settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
"apiv3",
"social_auth_local",
"im",
'newsletter'
]

PREREQ_APPS = [
Expand Down Expand Up @@ -311,7 +312,10 @@
'social_auth_local.pipeline.social_user',

# CUSTOM PIPELINE
'social_auth_local.pipeline.require_email',
# 'social_auth_local.pipeline.require_email',

# CUSTOM PIPELINE
'social_auth_local.pipeline.ask_for_newsletter',

# Make up a username for this person, appends a random string at the end if
# there's any collision.
Expand Down
Loading