Hey, On 11/04/2015 10:04 PM, Raphael Hertzog wrote: > Hello Orestis, > > sorry that I did not find the time to get back to you earlier. > no worries ;) > 1/ that the package page contains a link to the package news page (the > link you added in the header of the panel) > 2/ that the package page contains a link to the "?page=2" version of the > package news page when you have more news than the configured limit (and > possibly that you don't have any such link when you're below) ack for those 2 > 3/ the package news page does return the expected content for "?page=2" > just to ensure that pagination works and that we display something that > looks OK > Added a test, I am not sure though if that's what you were looking for.. Fixed the other suggestions as well.. Sending a patch for a flake8 issue. Cheers, Orestis
From e0163800224b5d3e490b6358b25cb37868031fd2 Mon Sep 17 00:00:00 2001
From: Orestis Ioannou <orestis@oioannou.com>
Date: Wed, 2 Sep 2015 10:38:10 +0200
Subject: [PATCH 1/2] Add support for browsing package news
Packages now have their own specific news page accessible by
the respective panel in the package page.
---
distro_tracker/core/panels.py | 6 ++-
.../core/templates/core/_news_pagination.html | 34 +++++++++++++++
.../core/templates/core/package_news.html | 8 ++++
.../core/templates/core/panels/news.html | 31 +++-----------
distro_tracker/core/tests/tests_panels.py | 43 +++++++++++++++++++
distro_tracker/core/tests/tests_views.py | 50 ++++++++++++++++++++++
distro_tracker/core/views.py | 28 ++++++++++++
distro_tracker/project/urls.py | 4 ++
8 files changed, 178 insertions(+), 26 deletions(-)
create mode 100644 distro_tracker/core/templates/core/_news_pagination.html
create mode 100644 distro_tracker/core/templates/core/package_news.html
diff --git a/distro_tracker/core/panels.py b/distro_tracker/core/panels.py
index 8c2cd82..08d3dc2 100644
--- a/distro_tracker/core/panels.py
+++ b/distro_tracker/core/panels.py
@@ -805,9 +805,11 @@ class NewsPanel(BasePanel):
def context(self):
news = News.objects.prefetch_related('signed_by')
news = news.filter(package=self.package).order_by('-datetime_created')
- news = news[:self.NEWS_LIMIT]
+ news = list(news[:self.NEWS_LIMIT])
+ more_pages = len(news) == self.NEWS_LIMIT
return {
- 'news': news
+ 'news': news,
+ 'has_more': more_pages
}
@property
diff --git a/distro_tracker/core/templates/core/_news_pagination.html b/distro_tracker/core/templates/core/_news_pagination.html
new file mode 100644
index 0000000..82d4f9a
--- /dev/null
+++ b/distro_tracker/core/templates/core/_news_pagination.html
@@ -0,0 +1,34 @@
+<ul class="list-group list-group-flush">
+ {% for news_item in news %}
+ <li class="list-group-item">
+ [<span class="news-date">{{ news_item.datetime_created|date:"Y-m-d" }}</span>]
+ <a href="{% url 'dtracker-news-page' news_item.pk %}">
+ <span class="news-title">{{ news_item.title }}</span>
+ </a>
+ {% if news_item.created_by %}(<span class="news-creator">{{ news_item.created_by }}</span>){% endif %}
+ {% with signers=news_item.signed_by.all %}
+ {% if signers and signers.0.name != news_item.created_by %}
+ {% spaceless %}
+ <span>(signed by: </span>
+ {% for signer in signers %}
+ <span class="news-signer">{{ signer.name }}</span>
+ {% if not forloop.last %}<span>, </span>{% endif %}
+ {% endfor %}
+ <span>)</span>
+ {% endspaceless %}
+ {% endif %}
+ {% endwith %}
+ </li>
+ {% endfor %}
+</ul>
+{% if is_paginated %}
+<div class="text-center">
+<div class="pagination">
+<ul>
+ {% for page in page_obj.paginator.page_range %}
+ <li {% if page_obj.number == page %}class="active"{% endif %}><a href="?page={{ page }}">{{ page }}</a></li>
+ {% endfor %}
+</ul>
+</div>
+</div>
+{% endif %}
diff --git a/distro_tracker/core/templates/core/package_news.html b/distro_tracker/core/templates/core/package_news.html
new file mode 100644
index 0000000..ed40eaf
--- /dev/null
+++ b/distro_tracker/core/templates/core/package_news.html
@@ -0,0 +1,8 @@
+{% extends 'core/base.html' %}
+
+{% block content %}
+<h1 class="text-center">News for package <a href="{% url 'dtracker-package-page' package %}">{{ package }}</a></h1>
+<div class="row-fluid">
+{% include "core/_news_pagination.html" %}
+</div>
+{% endblock %}
diff --git a/distro_tracker/core/templates/core/panels/news.html b/distro_tracker/core/templates/core/panels/news.html
index 1cc38fd..b0532ed 100644
--- a/distro_tracker/core/templates/core/panels/news.html
+++ b/distro_tracker/core/templates/core/panels/news.html
@@ -3,7 +3,7 @@
{% block panel-header %}
<div class="row-fluid">
<div class="pull-left">
- <span>{{ panel.title }}</span>
+ <span>{{ panel.title }} <a href="{% url 'dtracker-package-news' package.name %}"><i class='icon-share'></i></a></span>
</div>
<div class="pull-right">
<a title="rss feed" href="{% url 'dtracker-package-rss-news-feed' package.name %}">
@@ -14,27 +14,10 @@
{% endblock %}
{% block panel-body %}
-<ul class="list-group list-group-flush">
- {% for news_item in panel.context.news %}
- <li class="list-group-item">
- [<span class="news-date">{{ news_item.datetime_created|date:"Y-m-d" }}</span>]
- <a href="{% url 'dtracker-news-page' news_item.pk %}">
- <span class="news-title">{{ news_item.title }}</span>
- </a>
- {% if news_item.created_by %}(<span class="news-creator">{{ news_item.created_by }}</span>){% endif %}
- {% with signers=news_item.signed_by.all %}
- {% if signers and signers.0.name != news_item.created_by %}
- {% spaceless %}
- <span>(signed by: </span>
- {% for signer in signers %}
- <span class="news-signer">{{ signer.name }}</span>
- {% if not forloop.last %}<span>, </span>{% endif %}
- {% endfor %}
- <span>)</span>
- {% endspaceless %}
- {% endif %}
- {% endwith %}
- </li>
- {% endfor %}
-</ul>
+{% with news=panel.context.news %}
+ {% include "core/_news_pagination.html" %}
+{% endwith %}
+{% if panel.context.has_more %}
+<div class="pagination">Page <a href="{% url 'dtracker-package-news' package.name %}?page=2">2</a></div>
+{% endif %}
{% endblock %}
diff --git a/distro_tracker/core/tests/tests_panels.py b/distro_tracker/core/tests/tests_panels.py
index bf5c91b..f64a471 100644
--- a/distro_tracker/core/tests/tests_panels.py
+++ b/distro_tracker/core/tests/tests_panels.py
@@ -219,3 +219,46 @@ class DeadPackageWarningPanelTests(TestCase):
SourcePackageRepositoryEntry.objects.create(
source_package=self.srcpkg, repository=self.repo1)
self.assertFalse(self.panel.context['disappeared'])
+
+
+class NewsPanelTests(TestCase):
+ def setUp(self):
+ self.package = SourcePackageName.objects.create(name='dummy-package')
+ self.src_pkg = SourcePackage.objects.create(
+ source_package_name=self.package, version='1.0.0')
+ self.src_pkg.save()
+ # add some news
+ for i in range(29):
+ self.package.news_set.create(title="News {}".format(i),
+ created_by="Author {}".format(i))
+
+ def get_package_page_response(self):
+ url = reverse('dtracker-package-page', kwargs={
+ 'package_name': self.package.name,
+ })
+ return self.client.get(url)
+
+ def find_link_in_content(self, content, link):
+ """Helper method to parse response and verify link exists"""
+ html = soup(content)
+ for a_tag in html.findAll('a', {'href': True}):
+ if a_tag['href'] == link:
+ return True
+ return False
+
+ def test_news_links(self):
+ """Tests the links in the news panel"""
+ response = self.get_package_page_response()
+ news_link = reverse('dtracker-package-news', kwargs={
+ 'package_name': self.package.name})
+ # verify news-link is present
+ self.assertTrue(self.find_link_in_content(response.content, news_link))
+ # verify ?page=2 not present
+ self.assertFalse(self.find_link_in_content(response.content,
+ news_link + "?page=2"))
+ self.package.news_set.create(title="News {}".format(30),
+ created_by="Author {}".format(30))
+ # refresh package page to include new news item
+ response = self.get_package_page_response()
+ self.assertTrue(self.find_link_in_content(response.content,
+ news_link + "?page=2"))
diff --git a/distro_tracker/core/tests/tests_views.py b/distro_tracker/core/tests/tests_views.py
index e45c116..cde56be 100644
--- a/distro_tracker/core/tests/tests_views.py
+++ b/distro_tracker/core/tests/tests_views.py
@@ -25,6 +25,7 @@ import json
from django.core.urlresolvers import reverse
from django.conf import settings
+from bs4 import BeautifulSoup as soup
import os
@@ -477,3 +478,52 @@ class ActionItemJsonViewTest(TestCase):
}))
self.assertEqual(response.status_code, 404)
+
+
+class NewsViewTest(TestCase):
+ """
+ Tests for the :class:`distro_tracker.core.views.PackageNews`.
+ """
+ def setUp(self):
+ self.package = SourcePackageName.objects.create(name='dummy-package')
+ self.src_pkg = SourcePackage.objects.create(
+ source_package_name=self.package, version='1.0.0')
+ self.src_pkg.save()
+ # add some news
+ for i in range(61):
+ self.package.news_set.create(title="News {}".format(i),
+ created_by="Author {}".format(i))
+
+ def get_package_news(self, url, page=None):
+ if not page:
+ return self.client.get(url)
+ else:
+ return self.client.get('%s?page=%s' % (url, page))
+
+ def find_link_in_content(self, content, link):
+ """Helper method to parse response and verify link exists"""
+ html = soup(content)
+ for a_tag in html.findAll('a', {'href': True}):
+ if a_tag['href'] == link:
+ return True
+ return False
+
+ def test_news_page(self):
+ """ Tests that the second news page is what we expect"""
+ url = reverse('dtracker-package-news', kwargs={
+ 'package_name': self.package.name})
+ response = self.get_package_news(url)
+ # verify we can return to package page
+ package_url = reverse('dtracker-package-page', kwargs={
+ 'package_name': self.package.name,
+ })
+ self.assertTrue(self.find_link_in_content(response.content,
+ package_url))
+ # verify we only supply valid link pages
+ self.assertTrue(self.find_link_in_content(response.content,
+ '?page=2'))
+ self.assertFalse(self.find_link_in_content(response.content,
+ '?page=4'))
+ response = self.get_package_news(url, 2)
+ self.assertTrue(self.find_link_in_content(response.content,
+ '?page=1'))
diff --git a/distro_tracker/core/views.py b/distro_tracker/core/views.py
index 16b958b..593bb4b 100644
--- a/distro_tracker/core/views.py
+++ b/distro_tracker/core/views.py
@@ -176,6 +176,34 @@ def news_page(request, news_id):
})
+class PackageNews(ListView):
+ """
+ A view which lists all the news of a package.
+ """
+ _DEFAULT_NEWS_LIMIT = 30
+ NEWS_LIMIT = getattr(
+ settings,
+ 'DISTRO_TRACKER_NEWS_PANEL_LIMIT',
+ _DEFAULT_NEWS_LIMIT)
+
+ paginate_by = NEWS_LIMIT
+ template_name = 'core/package_news.html'
+ context_object_name = 'news'
+
+ def get(self, request, package_name):
+ self.package = get_object_or_404(PackageName, name=package_name)
+ return super(PackageNews, self).get(request, package_name)
+
+ def get_queryset(self):
+ news = self.package.news_set.prefetch_related('signed_by')
+ return news.order_by('-datetime_created')
+
+ def get_context_data(self, *args, **kwargs):
+ context = super(PackageNews, self).get_context_data(*args, **kwargs)
+ context['package'] = self.package
+ return context
+
+
class ActionItemJsonView(View):
"""
View renders a :class:`distro_tracker.core.models.ActionItem` in a JSON
diff --git a/distro_tracker/project/urls.py b/distro_tracker/project/urls.py
index cbe1fe8..0b21808 100644
--- a/distro_tracker/project/urls.py
+++ b/distro_tracker/project/urls.py
@@ -39,6 +39,7 @@ from distro_tracker.core.views import SetMuteTeamView
from distro_tracker.core.views import SetMembershipKeywords
from distro_tracker.core.views import EditMembershipView
from distro_tracker.core.views import IndexView
+from distro_tracker.core.views import PackageNews
from distro_tracker.core.news_feed import PackageNewsFeed
from distro_tracker.accounts.views import ConfirmAddAccountEmail
from distro_tracker.accounts.views import LoginView
@@ -213,6 +214,9 @@ urlpatterns = patterns(
url(r'^teams/(?P<slug>.+?)/$', TeamDetailsView.as_view(),
name='dtracker-team-page'),
+ # Package news page
+ url(r'^pkg/(?P<package_name>.+)/news/', PackageNews.as_view(),
+ name='dtracker-package-news'),
# Dedicated package page
url(r'^pkg/(?P<package_name>[^/]+)/?$',
--
2.6.2
From 93641f8e94451cc6f2d5e4e7046eba84e650514d Mon Sep 17 00:00:00 2001
From: Orestis Ioannou <orestis@oioannou.com>
Date: Tue, 10 Nov 2015 18:52:29 +0100
Subject: [PATCH 2/2] Fix flake8 issue
---
distro_tracker/vendor/debian/tracker_tasks.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/distro_tracker/vendor/debian/tracker_tasks.py b/distro_tracker/vendor/debian/tracker_tasks.py
index 6a7e5b2..617bdf1 100644
--- a/distro_tracker/vendor/debian/tracker_tasks.py
+++ b/distro_tracker/vendor/debian/tracker_tasks.py
@@ -424,8 +424,8 @@ class UpdatePackageBugStats(BaseTask):
src, package_name, bug_counts = line.split(':', 2)
else:
package_name, bug_counts = line.split(':', 1)
- # Merged counts are in parentheses so remove those before splitting
- # the numbers
+ # Merged counts are in parentheses so remove those before
+ # splitting the numbers
bug_counts = re.sub(r'[()]', ' ', bug_counts).split()
bug_counts = [int(count) for count in bug_counts]
except ValueError:
--
2.6.2
Attachment:
signature.asc
Description: OpenPGP digital signature