diff --git a/newsletter/__init__.py b/newsletter/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/newsletter/admin.py b/newsletter/admin.py
new file mode 100644
index 00000000..a81345d0
--- /dev/null
+++ b/newsletter/admin.py
@@ -0,0 +1,10 @@
+from django.contrib import admin
+
+from newsletter.models import NewsletterCache
+
+
+class NewsletterCacheAdmin(admin.ModelAdmin):
+ pass
+
+
+admin.site.register(NewsletterCache, NewsletterCacheAdmin)
diff --git a/newsletter/apps.py b/newsletter/apps.py
new file mode 100644
index 00000000..e4a2ad5b
--- /dev/null
+++ b/newsletter/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class WeeklyNewsletterConfig(AppConfig):
+ name = 'newsletter'
diff --git a/newsletter/management/__init__.py b/newsletter/management/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/newsletter/management/commands/__init__.py b/newsletter/management/commands/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/newsletter/management/commands/send_newsletter.py b/newsletter/management/commands/send_newsletter.py
new file mode 100644
index 00000000..019d5dbe
--- /dev/null
+++ b/newsletter/management/commands/send_newsletter.py
@@ -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()
diff --git a/newsletter/migrations/0001_initial.py b/newsletter/migrations/0001_initial.py
new file mode 100644
index 00000000..2b5389d3
--- /dev/null
+++ b/newsletter/migrations/0001_initial.py
@@ -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)),
+ ],
+ ),
+ ]
diff --git a/newsletter/migrations/0002_auto_20180219_0326.py b/newsletter/migrations/0002_auto_20180219_0326.py
new file mode 100644
index 00000000..bd9dda20
--- /dev/null
+++ b/newsletter/migrations/0002_auto_20180219_0326.py
@@ -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),
+ ),
+ ]
diff --git a/newsletter/migrations/0003_newslettercache_subscribes.py b/newsletter/migrations/0003_newslettercache_subscribes.py
new file mode 100644
index 00000000..7e4df564
--- /dev/null
+++ b/newsletter/migrations/0003_newslettercache_subscribes.py
@@ -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),
+ ),
+ ]
diff --git a/newsletter/migrations/__init__.py b/newsletter/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/newsletter/models.py b/newsletter/models.py
new file mode 100644
index 00000000..fa5d9fdb
--- /dev/null
+++ b/newsletter/models.py
@@ -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)
diff --git a/newsletter/services.py b/newsletter/services.py
new file mode 100644
index 00000000..446d86e4
--- /dev/null
+++ b/newsletter/services.py
@@ -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()
diff --git a/newsletter/templates/newsletter.html b/newsletter/templates/newsletter.html
new file mode 100644
index 00000000..42930b96
--- /dev/null
+++ b/newsletter/templates/newsletter.html
@@ -0,0 +1,119 @@
+
+
+
+ Happy email
+
+
+
+
+
+
diff --git a/newsletter/templates/newsletter.txt b/newsletter/templates/newsletter.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/newsletter/tests.py b/newsletter/tests.py
new file mode 100644
index 00000000..ae6200ee
--- /dev/null
+++ b/newsletter/tests.py
@@ -0,0 +1,6 @@
+from django.test import TestCase
+
+
+class NewsletterServiceTestCase(TestCase):
+ def test_get_user_favorite_projects(self):
+ self.fail()
diff --git a/newsletter/urls.py b/newsletter/urls.py
new file mode 100644
index 00000000..278c3519
--- /dev/null
+++ b/newsletter/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls import url
+
+from newsletter.views import unsubscribe, ask_for_newsletter
+
+urlpatterns = [
+ url(regex=r'^unsubscribe/(?P[\w.@+-]+)/(?P[\w.:\-_=]+)/$', view=unsubscribe, name='unsubscribe'),
+ url(regex=r'^ask/$', view=ask_for_newsletter, name='ask_for_newsletter'),
+]
diff --git a/newsletter/views.py b/newsletter/views.py
new file mode 100644
index 00000000..7e56e047
--- /dev/null
+++ b/newsletter/views.py
@@ -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,
+ }
diff --git a/settings/base.py b/settings/base.py
index af79d567..9bd46ee9 100644
--- a/settings/base.py
+++ b/settings/base.py
@@ -137,6 +137,7 @@
"apiv3",
"social_auth_local",
"im",
+ 'newsletter'
]
PREREQ_APPS = [
@@ -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.
diff --git a/social_auth_local/pipeline.py b/social_auth_local/pipeline.py
index 22b978c2..c7cce3f4 100644
--- a/social_auth_local/pipeline.py
+++ b/social_auth_local/pipeline.py
@@ -61,6 +61,15 @@ def require_email(strategy, details, user=None, is_new=False, *args, **kwargs):
)
+@partial
+def ask_for_newsletter(strategy, details, *args, **kwargs):
+ subscription = strategy.request_data().get('subscription')
+ if subscription:
+ details['subscription'] = subscription
+ current_partial = kwargs.get('current_partial')
+ return strategy.redirect('/newsletter/ask/?partial_token={}'.format(current_partial.token))
+
+
def save_profile_pipeline(backend, user, response, details, social, *args, **kwargs):
try:
# profile could be created for a user which previously logged in
diff --git a/social_auth_local/views.py b/social_auth_local/views.py
index 30676e14..bcb4376c 100644
--- a/social_auth_local/views.py
+++ b/social_auth_local/views.py
@@ -29,7 +29,6 @@ def merging_accounts(request):
}
-
@render_to('social_auth_local/email_required.html')
def require_email(request):
"""Email required page"""
diff --git a/templates/social_auth_local/ask_for_newsletter.html b/templates/social_auth_local/ask_for_newsletter.html
new file mode 100644
index 00000000..c7815320
--- /dev/null
+++ b/templates/social_auth_local/ask_for_newsletter.html
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% load i18n static %}
+
+{% block head_title %}{% trans "Email Required" %}{% endblock %}
+
+{% block body_class %}email_required{% endblock %}
+
+{% block extra_head %}
+
+{% endblock %}
+
+{% block body %}
+
+
Newsletter
+
+
+{% endblock %}
+
diff --git a/urls.py b/urls.py
index bbeee78a..9d2be922 100644
--- a/urls.py
+++ b/urls.py
@@ -36,6 +36,7 @@
url(r"^projects/", include("package.urls")),
url(r"^grids/", include("grid.urls")),
url(r"^feeds/", include("feeds.urls")),
+ url(r'^newsletter/', include('newsletter.urls')),
url(r"^categories/(?P[-\w]+)/$", category, name="category"),
url(r"^categories/$", homepage, name="categories"),