diff --git a/development.ini b/development.ini index 722b41e..e0363a2 100644 --- a/development.ini +++ b/development.ini @@ -8,6 +8,10 @@ use = egg:springboard unicore.content_repo_url = https://github.com/universalcore/unicore-cms-content-gem-tanzania ; thumbor.security_key = '' +[celery] +CELERY_TASK_SERIALIZER = json +CELERY_ALWAYS_EAGER = True + ### # wsgi server configuration ### diff --git a/requirements-dev.txt b/requirements-dev.txt index d1ed8da..2ace77f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,4 +8,5 @@ pytest-xdist Sphinx sphinx_rtd_theme sphinx-argparse +mock -r requirements.txt \ No newline at end of file diff --git a/springboard/config.py b/springboard/config.py index 26e1fc9..47ea2b1 100644 --- a/springboard/config.py +++ b/springboard/config.py @@ -6,3 +6,4 @@ def includeme(config): config.add_route('flat_page', '/{slug}/') config.add_route('api_notify', '/api/notify/', request_method='POST') config.scan(".views") + config.scan(".events") diff --git a/springboard/defaults.ini b/springboard/defaults.ini index 9582cdb..0c38488 100644 --- a/springboard/defaults.ini +++ b/springboard/defaults.ini @@ -18,6 +18,3 @@ jinja2.filters = format_date = springboard.filters:format_date_filter thumbor = springboard.filters:thumbor_filter markdown = springboard.filters:markdown_filter - -[celery] -CELERY_TASK_SERIALIZER = json diff --git a/springboard/events.py b/springboard/events.py new file mode 100644 index 0000000..6d450e2 --- /dev/null +++ b/springboard/events.py @@ -0,0 +1,31 @@ +from uuid import uuid4 + +from pyramid.events import NewResponse +from pyramid.events import subscriber + +from unicore.google.tasks import pageview + + +ONE_YEAR = 31556952 + + +@subscriber(NewResponse) +def new_request(event): + request = event.request + registry = request.registry + response = event.response + + profile_id = registry.settings.get('ga.profile_id') + if not profile_id: + return + + client_id = request.cookies.get('ga_client_id', uuid4().hex) + response.set_cookie('ga_client_id', value=client_id, max_age=ONE_YEAR) + pageview.delay(profile_id, client_id, { + 'path': request.path, + 'uip': request.remote_addr, + 'dr': request.referer or '', + 'dh': request.domain, + 'user_agent': request.user_agent, + 'ul': request.accept_language, + }) diff --git a/springboard/filters.py b/springboard/filters.py index 03a4e1b..2e018ab 100644 --- a/springboard/filters.py +++ b/springboard/filters.py @@ -10,9 +10,10 @@ @contextfilter -def format_date_filter(ctx, date_string, format): - dt = parser.parse(date_string) - return dt.strftime(format) +def format_date_filter(ctx, timestamp, format): + if isinstance(timestamp, basestring): + timestamp = parser.parse(timestamp) + return timestamp.strftime(format) @contextfilter @@ -28,4 +29,6 @@ def thumbor_filter(ctx, image, width, height=None): @contextfilter def markdown_filter(ctx, content): + if not content: + return content return markdown(content) diff --git a/springboard/tests/test_events.py b/springboard/tests/test_events.py new file mode 100644 index 0000000..c8b4687 --- /dev/null +++ b/springboard/tests/test_events.py @@ -0,0 +1,56 @@ +from datetime import datetime + +import mock + +from springboard.tests.base import SpringboardTestCase + + +class TestEvents(SpringboardTestCase): + + def setUp(self): + self.workspace = self.mk_workspace() + self.app = self.mk_app(self.workspace, settings={ + 'ga.profile_id': 'UA-some-id', + }) + + @mock.patch('unicore.google.tasks.pageview.delay') + def test_ga_pageviews(self, mock_task): + [category] = self.mk_categories(self.workspace, count=1) + [page] = self.mk_pages(self.workspace, count=1, + primary_category=category.uuid, + created_at=datetime.now().isoformat()) + self.workspace.save(page, 'Add page') + self.workspace.refresh_index() + + self.app.get('/', status=200, extra_environ={ + 'HTTP_HOST': 'some.site.com', + 'REMOTE_ADDR': '192.0.0.1', + }) + mock_task.assert_called_once() + ((profile_id, gen_client_id, data), _) = mock_task.call_args_list[0] + self.assertEqual(profile_id, 'UA-some-id') + self.assertEqual(data['path'], '/') + self.assertEqual(data['uip'], '192.0.0.1') + self.assertEqual(data['dh'], 'some.site.com') + self.assertEqual(data['dr'], '') + + page_url = '/page/%s/' % page.uuid + headers = { + 'User-agent': 'Mozilla/5.0', + } + self.app.get(page_url, status=200, headers=headers, extra_environ={ + 'HTTP_REFERER': '/', + 'HTTP_HOST': 'some.site.com', + 'REMOTE_ADDR': '192.0.0.1', + }) + ((profile_id, client_id, data), _) = mock_task.call_args_list[1] + + self.assertEqual(profile_id, 'UA-some-id') + self.assertEqual(data['path'], page_url) + self.assertEqual(data['dr'], '/') + self.assertEqual(data['uip'], '192.0.0.1') + self.assertEqual(data['dh'], 'some.site.com') + self.assertEqual(data['user_agent'], 'Mozilla/5.0') + + # # ensure cid is the same across calls + self.assertEqual(gen_client_id, client_id) diff --git a/springboard/tests/test_filters.py b/springboard/tests/test_filters.py index 897ade4..88c76f9 100644 --- a/springboard/tests/test_filters.py +++ b/springboard/tests/test_filters.py @@ -1,3 +1,5 @@ +from datetime import datetime + from pyramid import testing from libthumbor import CryptoURL @@ -12,11 +14,16 @@ class TestFilters(SpringboardTestCase): def setUp(self): self.workspace = self.mk_workspace() - def test_format_date_filter(self): + def test_format_date_filter_string(self): self.assertEqual( format_date_filter({}, '2012-12-31', '%d-%m-%y'), '31-12-12') + def test_format_date_filter_date(self): + self.assertEqual( + format_date_filter({}, datetime(2012, 12, 31), '%d-%m-%y'), + '31-12-12') + def test_thumbor_filter(self): testing.setUp(settings={ 'thumbor.security_key': 'foo', @@ -42,3 +49,7 @@ def test_markdown_filter(self): self.assertEqual( markdown_filter({}, '*foo*'), '

foo

') + + def test_markdown_filter_none(self): + self.assertEqual(markdown_filter({}, None), None) + self.assertEqual(markdown_filter({}, ''), '') diff --git a/springboard/tools/commands/cookiecutter/startapp/{{cookiecutter.app_name}}/development.ini b/springboard/tools/commands/cookiecutter/startapp/{{cookiecutter.app_name}}/development.ini index 4f43625..aff8e2b 100644 --- a/springboard/tools/commands/cookiecutter/startapp/{{cookiecutter.app_name}}/development.ini +++ b/springboard/tools/commands/cookiecutter/startapp/{{cookiecutter.app_name}}/development.ini @@ -8,6 +8,10 @@ use = egg:{{cookiecutter.app_name}} unicore.content_repo_url = {{cookiecutter.unicore_content_repo_url}} ; thumbor.security_key = '{{cookiecutter.thumbor_security_key}}' +[celery] +CELERY_TASK_SERIALIZER = json +CELERY_ALWAYS_EAGER = True + ### # wsgi server configuration ###