diff --git a/requirements.txt b/requirements.txt
index 26c7d07..18d0948 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1 +1 @@
-springboard
\ No newline at end of file
+springboard>=1.0.0
diff --git a/springboard_iogt/application.py b/springboard_iogt/application.py
index 1694536..87ea382 100644
--- a/springboard_iogt/application.py
+++ b/springboard_iogt/application.py
@@ -13,6 +13,11 @@ def main(global_config, **settings):
defaults.update(settings)
config = Configurator(settings=defaults)
+
+ # override springboard routes
+ config.add_route('home', '/')
+ config.scan('.views')
+
config.include('springboard.config')
config.override_asset(
to_override='springboard:templates/',
diff --git a/springboard_iogt/templates/home.jinja2 b/springboard_iogt/templates/home.jinja2
index 48a3554..d7963a7 100644
--- a/springboard_iogt/templates/home.jinja2
+++ b/springboard_iogt/templates/home.jinja2
@@ -15,41 +15,26 @@
- {% set featured_pages = all_pages.filter(language=language, featured=True) %}
- {% if featured_pages %}
-
-
{{gettext('Latest')}}
- {% for page in featured_pages %}
+ {% for category, page in recent_content(limit=8) %}
+
+ {% if category %}
+
+ {% endif %}
- {% endfor %}
-
- {% endif %}
-
- {% for category in all_categories.filter(language=language) %}
- {% set category_pages = all_pages.filter(primary_category=category.uuid) %}
- {% if category_pages %}
-
-
- {% for page in category_pages %}
-
-
-
{{page.description}}
-
-
- {% endfor %}
+ {% if category %}
+ {% endif %}
- {% endif %}
{% endfor %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/springboard_iogt/tests/__init__.py b/springboard_iogt/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/springboard_iogt/tests/test_views.py b/springboard_iogt/tests/test_views.py
new file mode 100644
index 0000000..9fa88f6
--- /dev/null
+++ b/springboard_iogt/tests/test_views.py
@@ -0,0 +1,70 @@
+import re
+
+from datetime import datetime, timedelta
+
+from pyramid import testing
+
+from springboard.tests import SpringboardTestCase
+
+from springboard_iogt.views import IoGTViews
+from springboard_iogt.application import main
+
+
+class TestIoGTViews(SpringboardTestCase):
+
+ def setUp(self):
+ self.workspace = self.mk_workspace()
+ self.config = testing.setUp(settings={
+ 'unicore.repos_dir': self.working_dir,
+ 'unicore.content_repo_urls': self.workspace.working_dir,
+ })
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_recent_content(self):
+ category_p1, category_p3 = self.mk_categories(self.workspace, count=2)
+ [page1] = self.mk_pages(
+ self.workspace, count=1,
+ primary_category=category_p1.uuid,
+ created_at=datetime.utcnow().isoformat())
+ [page2] = self.mk_pages(
+ self.workspace, count=1,
+ primary_category=None,
+ created_at=(datetime.utcnow() - timedelta(hours=1)).isoformat())
+ [page3] = self.mk_pages(
+ self.workspace, count=1,
+ primary_category=category_p3.uuid,
+ created_at=(datetime.utcnow() - timedelta(hours=2)).isoformat())
+ views = IoGTViews(self.mk_request())
+
+ results = views.recent_content()(limit=2)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(
+ {(category_p1.uuid, page1.uuid), (None, page2.uuid)},
+ set((c.uuid if c else None, p.uuid) for c, p in results))
+
+ results = views.recent_content()(limit=3)
+ self.assertEqual(len(results), 3)
+ self.assertEqual(
+ {(category_p1.uuid, page1.uuid), (None, page2.uuid),
+ (category_p3.uuid, page3.uuid)},
+ set((c.uuid if c else None, p.uuid) for c, p in results))
+
+ def test_index_view(self):
+ [category] = self.mk_categories(self.workspace, count=1)
+ [page1, page2] = self.mk_pages(
+ self.workspace, count=2,
+ created_at=datetime.utcnow().isoformat())
+ page1 = page1.update({'primary_category': category.uuid})
+ self.workspace.save(page1, 'Update page category')
+ self.workspace.refresh_index()
+ app = self.mk_app(self.workspace, main=main)
+
+ response = app.get('/')
+ self.assertEqual(response.status_int, 200)
+ html = response.html
+ re_page_url = re.compile(r'/page/.{32}/')
+ re_category_url = re.compile(r'/category/.{32}/')
+ self.assertEqual(len(html.find_all('a', href=re_page_url)), 2)
+ self.assertEqual(len(html.find_all('a', href=re_category_url)), 2)
diff --git a/springboard_iogt/utils.py b/springboard_iogt/utils.py
new file mode 100644
index 0000000..ae9b7de
--- /dev/null
+++ b/springboard_iogt/utils.py
@@ -0,0 +1,8 @@
+def randomize_query(s_obj, seed=None):
+ return s_obj.query_raw({
+ "function_score": {
+ "functions": [
+ {"random_score": {"seed": seed}}
+ ],
+ "score_mode": "sum"
+ }})
diff --git a/springboard_iogt/views.py b/springboard_iogt/views.py
new file mode 100644
index 0000000..dfe3418
--- /dev/null
+++ b/springboard_iogt/views.py
@@ -0,0 +1,65 @@
+import random
+from datetime import datetime
+from itertools import chain
+
+from pyramid.view import view_config
+from elasticutils import F
+
+from springboard.views.base import SpringboardViews
+
+from springboard_iogt.utils import randomize_query
+
+
+class IoGTViews(SpringboardViews):
+
+ @view_config(route_name='home',
+ renderer='springboard_iogt:templates/home.jinja2')
+ def index_view(self):
+ return self.context(recent_content=self.recent_content())
+
+ def recent_content(self):
+ # get 2 most recent pages and their categories
+ [page1, page2] = self.all_pages.filter(
+ language=self.language).order_by('-created_at')[:2]
+ categories = self.all_categories.filter(
+ uuid__in=filter(
+ None, [page1.primary_category, page2.primary_category]))
+ categories = dict((category.uuid, category) for category in categories)
+ category1 = categories.get(page1.primary_category)
+ category2 = categories.get(page2.primary_category)
+
+ # random seed that changes hourly
+ seed = datetime.utcnow().hour
+
+ # get random categories and exclude categories of 2 most recent pages
+ categories = self.all_categories.filter(
+ ~F(uuid__in=categories.keys()), language=self.language)
+ categories = randomize_query(categories, seed=seed)
+ # filter to exclude the 2 most recent pages
+ f_exclude_pages = ~F(uuid__in=[page1.uuid, page2.uuid])
+
+ def do_query(limit):
+ most_recent = [(category1, page1), (category2, page2)]
+ most_recent_per_category = []
+ # NOTE: this is bad if limit is large
+ # We are fetching pages individually, because we
+ # can't use facets or aggregates.
+ for category in categories[:limit - 2]:
+ try:
+ [page] = self.all_pages.filter(
+ f_exclude_pages,
+ primary_category=category.uuid).order_by(
+ '-created_at')[:1]
+ most_recent_per_category.append((category, page))
+ except ValueError:
+ pass
+
+ results = [(c.to_object() if c else None, p.to_object())
+ for c, p
+ in chain(most_recent, most_recent_per_category)]
+ # randomize position of most_recent content
+ random.seed(seed)
+ random.shuffle(results)
+ return results
+
+ return do_query