- Contributions will be listed on our donation page, with your name and - photo (from Gravatar). You can choose to donate privately if you do not wish to be listed. - We do not store your credit card details, - payment is processed directly through Stripe. -
- - -{% endblock %} diff --git a/readthedocs/donate/templates/donate/ethicalads-success.html b/readthedocs/donate/templates/donate/ethicalads-success.html deleted file mode 100644 index caf2f7a7b3b..00000000000 --- a/readthedocs/donate/templates/donate/ethicalads-success.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} -{% load static %} - -{% block title %}{% trans "Sponsorship" %}{% endblock %} - -{% block content %} -- We think that our Ethical Advertising campaign is a wonderful step forward for running ads on open source projects. - Thanks for your support. -
-{% endblock %} diff --git a/readthedocs/donate/templates/donate/ethicalads.html b/readthedocs/donate/templates/donate/ethicalads.html deleted file mode 100644 index 64cbe75f537..00000000000 --- a/readthedocs/donate/templates/donate/ethicalads.html +++ /dev/null @@ -1,92 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} -{% load static %} - -{% block title %}{% trans "Pay for your ad" %}{% endblock %} - -{% block extra_links %} - -{% endblock %} - -{% block extra_scripts %} - - - - -{% endblock %} - - -{% block content %} -- This form can be used to pay for your sponsorship of Read the Docs. - We think that our Ethical Advertising campaign is a wonderful step forward for running ads on open source projects. - You can pay for these ads below. -
- - -{% endblock %} diff --git a/readthedocs/donate/templates/donate/list.html b/readthedocs/donate/templates/donate/list.html deleted file mode 100644 index 0faf774f1ab..00000000000 --- a/readthedocs/donate/templates/donate/list.html +++ /dev/null @@ -1,136 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} -{% load gravatar %} - -{% block title %}{% trans "Sustainability" %}{% endblock %} - -{% block content %} - - {% include 'donate/progress.html' with evergreen=True %} - - -- Read the Docs has grown substantially - since its beginning as a weekend project. - Today, we: -
- -- We host projects from all languages and programming communities. - Some examples of projects that we host include: -
- -- Because of the growth and popularity of the service, the associated - technical and operational support demands a good deal of labor. - Where development can be sustained with community contributions, - consistent support for users, maintenance, and on-call procedures - require accountability and dedicated engineers. - These types of roles are hard to accomplish on a volunteer basis. -
- -- To make sure these roles were filled, we decided to treat the project as - a job – we formed a company and dedicated ourselves full-time to - the project. - Now, we would like to make sure that the service is sustainable - and its costs are covered. -
- -- We are asking for your contributions to ensure continued operational - and technical support for our community, as well as continued - development. -
- -- Our primary goal is to cover the costs of supporting and maintaining - Read the Docs for the next three months. - We hope to account for the cost of at least one full-time engineer, - which, at the rate of $50/hour, would - cost $24,000 over the next three months. - Covering labor costs would allow us to spend time supporting the site - instead of seeking income elsewhere. -
- - - - -- Our servers are graciously sponsored by Rackspace, at a value of $2,000 each month. - They are our single largest sponsor, - and Read the Docs wouldn't be possible without their generous support. - You can view the full list of past sponsors in our documentation. -
- -- If you are a company that shares our passion for documentation, - or if you want to support our mission, - contact us for more information on sponsoring Read the Docs. -
- - - -- Below is a list of all the wonderful people who have supported our - vision. -
- -- This program is modeled after the fantastic Django Fellowship. - Thanks for the inspiration. -
- -{% endblock %} diff --git a/readthedocs/donate/templates/donate/progress.html b/readthedocs/donate/templates/donate/progress.html deleted file mode 100644 index 9dcd16cf436..00000000000 --- a/readthedocs/donate/templates/donate/progress.html +++ /dev/null @@ -1,68 +0,0 @@ -{% load i18n %} - -- Since its inception over four years ago, Read the Docs has grown - into a massive resource that developers rely on daily. To - make Read the Docs sustainable, and to - improve support for our users and readers, we are asking the community - for contributions. -
-- - If you consider Read the Docs to be a useful service, and would - like to see it continue to grow, consider pitching in to - help cover the cost of bringing free and open documentation to - everyone. - -
- - {% if read_more %} - - {% endif %} -{{ suggestion.message }}
- {% ifequal suggestion.type 'top' %} -- Go to the top of the documentation. -
- {% endifequal %} - {% ifequal suggestion.type 'list' %} --Read the Docs is sponsored by Sentry, which gives developers the tools to keep calm when things catch fire. -
-- . - ":" - ___:____ |"\/"| - ,' `. \ / - | O \___/ | - ~^~^~^~^~^~^~^~^~^~^~^~^~ - - Fail. Check back in a bit! - -- - - - -{% if request.sentry.id %} - -{% endif %} - -
-Read the Docs is sponsored by Sentry, which gives developers the tools to keep calm when things catch fire. -
- -{% endblock %} diff --git a/readthedocs/donate/templates/donate/promo_detail.html b/readthedocs/donate/templates/donate/promo_detail.html deleted file mode 100644 index 027dd107cc2..00000000000 --- a/readthedocs/donate/templates/donate/promo_detail.html +++ /dev/null @@ -1,112 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} -{% load static %} - -{% block title %}{% trans "Promo Detail" %}{% endblock %} - -{% block extra_links %} - -{% endblock %} - -{% block content %} - --Total Clicks for all shown promos: {{ total_clicks }} -
-{% endif %} - -Day (UTC) | -Views | -Clicks | -CTR | -
---|---|---|---|
{{ day.date }} | -{{ day.views }} |
- {{ day.clicks }} |
- {{ day.click_ratio }}% |
-
Total (over all time) | -{{ promo.total_views }} | -{{ promo.total_clicks }} | -{{ promo.total_click_ratio }}% | -
- We appreciate your contribution greatly, thank you for showing your support! - Your help will go a long ways towards making the service more sustainable. -
- -- You can help us get more contributions by spreading news of our campaign - and sharing the following link: -
-- Help Support Read the Docs -
- - Tweet - - -{% endblock %} diff --git a/readthedocs/donate/tests.py b/readthedocs/donate/tests.py deleted file mode 100644 index 7faa7307109..00000000000 --- a/readthedocs/donate/tests.py +++ /dev/null @@ -1,273 +0,0 @@ -from __future__ import absolute_import - -import json -import mock - -from builtins import range -from django.contrib.auth.models import User -from django.test import TestCase -from django.core.urlresolvers import reverse -from django.core.cache import cache -from django.test.client import RequestFactory -from django_dynamic_fixture import get - -from ..core.middleware import FooterNoSessionMiddleware -from .models import SupporterPromo, GeoFilter, Country -from .constants import (CLICKS, VIEWS, OFFERS, - INCLUDE, EXCLUDE, READTHEDOCS_THEME) -from .signals import show_to_geo, get_promo, choose_promo, show_to_programming_language -from readthedocs.projects.models import Project - - -class PromoTests(TestCase): - - def setUp(self): - self.promo = get(SupporterPromo, - slug='promo-slug', - link='http://example.com', - image='http://media.example.com/img.png') - self.pip = get(Project, slug='pip', allow_promos=True) - - def test_clicks(self): - hash_key = 'random_hash' - cache.set(self.promo.cache_key(type=CLICKS, hash=hash_key), 0) - resp = self.client.get( - 'http://testserver/sustainability/click/%s/%s/' % (self.promo.id, hash_key)) - self.assertEqual(resp._headers['location'][1], 'http://example.com') - promo = SupporterPromo.objects.get(pk=self.promo.pk) - impression = promo.impressions.first() - self.assertEqual(impression.clicks, 1) - - def test_views(self): - cache.set(self.promo.cache_key(type=VIEWS, hash='random_hash'), 0) - resp = self.client.get( - 'http://testserver/sustainability/view/%s/random_hash/' % self.promo.id) - self.assertEqual(resp._headers['location'][1], 'http://media.example.com/img.png') - promo = SupporterPromo.objects.get(pk=self.promo.pk) - impression = promo.impressions.first() - self.assertEqual(impression.views, 1) - - def test_project_clicks(self): - cache.set(self.promo.cache_key(type=CLICKS, hash='random_hash'), 0) - cache.set(self.promo.cache_key(type='project', hash='random_hash'), self.pip.slug) - self.client.get('http://testserver/sustainability/click/%s/random_hash/' % self.promo.id) - promo = SupporterPromo.objects.get(pk=self.promo.pk) - impression = promo.project_impressions.first() - self.assertEqual(impression.clicks, 1) - - def test_stats(self): - for x in range(50): - self.promo.incr(OFFERS) - for x in range(20): - self.promo.incr(VIEWS) - for x in range(3): - self.promo.incr(CLICKS) - self.assertAlmostEqual(self.promo.view_ratio(), 40.0) - self.assertEqual(self.promo.click_ratio(), '15.000') - - def test_multiple_hash_usage(self): - cache.set(self.promo.cache_key(type=VIEWS, hash='random_hash'), 0) - self.client.get('http://testserver/sustainability/view/%s/random_hash/' % self.promo.id) - promo = SupporterPromo.objects.get(pk=self.promo.pk) - impression = promo.impressions.first() - self.assertEqual(impression.views, 1) - - # Don't increment again. - self.client.get('http://testserver/sustainability/view/%s/random_hash/' % self.promo.id) - promo = SupporterPromo.objects.get(pk=self.promo.pk) - impression = promo.impressions.first() - self.assertEqual(impression.views, 1) - - def test_invalid_id(self): - resp = self.client.get('http://testserver/sustainability/view/invalid/data/') - self.assertEqual(resp.status_code, 404) - - def test_invalid_hash(self): - cache.set(self.promo.cache_key(type=VIEWS, hash='valid_hash'), 0) - resp = self.client.get( - 'http://testserver/sustainability/view/%s/invalid_hash/' % self.promo.id) - promo = SupporterPromo.objects.get(pk=self.promo.pk) - self.assertEqual(promo.impressions.count(), 0) - self.assertEqual(resp._headers['location'][1], 'http://media.example.com/img.png') - - -class FilterTests(TestCase): - - def setUp(self): - us = Country.objects.all().filter(country='US').get() - ca = Country.objects.all().filter(country='CA').get() - mx = Country.objects.all().filter(country='MX').get() - az = Country.objects.all().filter(country='AZ').get() - - # Only show in US,CA - self.promo = get(SupporterPromo, - slug='promo-slug', - link='http://example.com', - live=True, - programming_language='py', - image='http://media.example.com/img.png' - ) - self.filter = get(GeoFilter, - promo=self.promo, - countries=[us, ca, mx], - filter_type=INCLUDE, - ) - - # Don't show in AZ - self.promo2 = get(SupporterPromo, - slug='promo2-slug', - link='http://example.com', - live=True, - programming_language='js', - image='http://media.example.com/img.png') - self.filter2 = get(GeoFilter, - promo=self.promo2, - countries=[az], - filter_type=EXCLUDE, - ) - - self.pip = get(Project, slug='pip', allow_promos=True, programming_language='py') - - def test_include(self): - # US view - ret = show_to_geo(self.promo, 'US') - self.assertTrue(ret) - - ret = show_to_geo(self.promo2, 'US') - self.assertTrue(ret) - - def test_exclude(self): - # Az -- don't show AZ ad - ret = show_to_geo(self.promo, 'AZ') - self.assertFalse(ret) - - ret = show_to_geo(self.promo2, 'AZ') - self.assertFalse(ret) - - def test_non_included_data(self): - # Random Country -- don't show "only US" ad - ret = show_to_geo(self.promo, 'FO') - self.assertFalse(ret) - - # Country FO is not excluded - ret2 = show_to_geo(self.promo2, 'FO') - self.assertTrue(ret2) - - def test_get_promo(self): - ret = get_promo('US', 'py', READTHEDOCS_THEME) - self.assertEqual(ret, self.promo) - - ret = get_promo('MX', 'py', READTHEDOCS_THEME) - self.assertEqual(ret, self.promo) - - ret = get_promo('FO', 'js', READTHEDOCS_THEME) - self.assertEqual(ret, self.promo2) - - ret = get_promo('AZ', 'js', READTHEDOCS_THEME) - self.assertEqual(ret, None) - - ret = get_promo('RANDOM', 'js', READTHEDOCS_THEME) - self.assertEqual(ret, self.promo2) - - def test_programming_language(self): - ret = show_to_programming_language(self.promo, 'py') - self.assertTrue(ret) - - ret = show_to_programming_language(self.promo, 'js') - self.assertFalse(ret) - - # This promo is JS only - ret = show_to_programming_language(self.promo2, 'py') - self.assertFalse(ret) - - ret = show_to_programming_language(self.promo2, 'js') - self.assertTrue(ret) - - -class ProbabilityTests(TestCase): - - def setUp(self): - # Only show in US,CA - self.promo = get(SupporterPromo, - slug='promo-slug', - link='http://example.com', - live=True, - image='http://media.example.com/img.png', - sold_impressions=1000, - ) - - # Don't show in AZ - self.promo2 = get(SupporterPromo, - slug='promo2-slug', - link='http://example.com', - live=True, - image='http://media.example.com/img.png', - sold_impressions=1000 * 1000, - ) - self.promo_list = [self.promo, self.promo2] - - def test_choose(self): - # US view - - promo_prob = self.promo.views_needed_today() - promo2_prob = self.promo2.views_needed_today() - total = promo_prob + promo2_prob - - with mock.patch('random.randint') as randint: - - randint.return_value = -1 - ret = choose_promo(self.promo_list) - self.assertEqual(ret, None) - - randint.return_value = 5 - ret = choose_promo(self.promo_list) - self.assertEqual(ret, self.promo) - - randint.return_value = promo_prob - 1 - ret = choose_promo(self.promo_list) - self.assertEqual(ret, self.promo) - - randint.return_value = promo_prob - ret = choose_promo(self.promo_list) - self.assertEqual(ret, self.promo) - - randint.return_value = promo_prob + 1 - ret = choose_promo(self.promo_list) - self.assertEqual(ret, self.promo2) - - randint.return_value = total - 1 - ret = choose_promo(self.promo_list) - self.assertEqual(ret, self.promo2) - - randint.return_value = total - ret = choose_promo(self.promo_list) - self.assertEqual(ret, self.promo2) - - randint.return_value = total + 1 - ret = choose_promo(self.promo_list) - self.assertEqual(ret, None) - - -class CookieTests(TestCase): - - def setUp(self): - self.promo = get(SupporterPromo, live=True) - - def test_no_cookie(self): - mid = FooterNoSessionMiddleware() - factory = RequestFactory() - - # Setup - cache.set(self.promo.cache_key(type=VIEWS, hash='random_hash'), 0) - request = factory.get( - 'http://testserver/sustainability/view/%s/random_hash/' % self.promo.id - ) - - # Null session here - mid.process_request(request) - self.assertEqual(request.session, {}) - - # Proper session here - home_request = factory.get('/') - mid.process_request(home_request) - self.assertTrue(home_request.session.TEST_COOKIE_NAME, 'testcookie') diff --git a/readthedocs/donate/urls.py b/readthedocs/donate/urls.py deleted file mode 100644 index 50fa8d9c0ec..00000000000 --- a/readthedocs/donate/urls.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Django URL patterns for the donate app.""" - -from __future__ import absolute_import -from django.conf.urls import url - -from .views import DonateCreateView, DonateListView, DonateSuccessView -from .views import PayAdsView, PaySuccess, PromoDetailView -from .views import click_proxy, view_proxy - - -urlpatterns = [ - url(r'^$', DonateListView.as_view(), name='donate'), - url(r'^pay/$', PayAdsView.as_view(), name='pay_ads'), - url(r'^pay/success/$', PaySuccess.as_view(), name='pay_success'), - url(r'^report/(?PRead the Docs is funded by readers like you. Help keep the site alive and well by supporting us with a Gold Subscription. - You can also make one-time donations on our sustainability page.
Hosting for the project is graciously provided by Rackspace. diff --git a/readthedocs/urls.py b/readthedocs/urls.py index a123524d1f2..afaec524ec3 100644 --- a/readthedocs/urls.py +++ b/readthedocs/urls.py @@ -44,6 +44,7 @@ url(r'^accounts/', include('readthedocs.profiles.urls.private')), url(r'^accounts/', include('allauth.urls')), url(r'^notifications/', include('readthedocs.notifications.urls')), + url(r'^accounts/gold/', include('readthedocs.gold.urls')), # For redirects url(r'^builds/', include('readthedocs.builds.urls')), # For testing the 404's with DEBUG on. @@ -83,11 +84,10 @@ groups = [basic_urls, rtd_urls, project_urls, api_urls, core_urls, i18n_urls, deprecated_urls] -if 'readthedocs.donate' in settings.INSTALLED_APPS: +if 'readthedocsext.donate' in settings.INSTALLED_APPS: # Include donation URL's groups.append([ - url(r'^sustainability/', include('readthedocs.donate.urls')), - url(r'^accounts/gold/', include('readthedocs.gold.urls')), + url(r'^sustainability/', include('readthedocsext.donate.urls')), ]) if not getattr(settings, 'USE_SUBDOMAIN', False) or settings.DEBUG: groups.insert(0, docs_urls) diff --git a/requirements/pip.txt b/requirements/pip.txt index 782dac3c8ea..caa6b917067 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -25,8 +25,6 @@ slumber==0.7.1 lxml==3.3.5 defusedxml==0.5.0 -django-countries==3.4.1 - # Basic tools redis==2.10.3 celery==3.1.23