From bfe075635168516038ed77ef9122bd7eca75a45e Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Thu, 25 Oct 2012 21:29:53 +0000 Subject: [PATCH] Added a simple page to show release information parsed from a release's changelog file. - Legacy-Id: 4958 --- changelog.py | 66 +++++++++++++++++++++++++++++ ietf/release/__init__.py | 0 ietf/release/models.py | 1 + ietf/release/tests.py | 16 +++++++ ietf/release/views.py | 22 ++++++++++ ietf/templates/release/release.html | 23 ++++++++++ ietf/urls.py | 36 ++++++++-------- 7 files changed, 145 insertions(+), 19 deletions(-) create mode 100644 changelog.py create mode 100644 ietf/release/__init__.py create mode 100644 ietf/release/models.py create mode 100644 ietf/release/tests.py create mode 100644 ietf/release/views.py create mode 100644 ietf/templates/release/release.html diff --git a/changelog.py b/changelog.py new file mode 100644 index 000000000..af23b06fb --- /dev/null +++ b/changelog.py @@ -0,0 +1,66 @@ +import re +from tzparse import tzparse +from datetime import datetime as Datetime + +def parse_date(dstr): + formats = [ + "%d %b %Y %H:%M:%S %Z", # standard logfile format + "%d %b %Y %H:%M:%S", + "%d %b %Y %H:%M %Z", + "%d %b %Y %H:%M", + "%d %b %Y %Z", + "%Y-%m-%d %H:%M:%S %Z", + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%d_%H:%M:%S %Z", + "%Y-%m-%d_%H:%M:%S", + "%Y-%m-%dT%H:%M:%S%Z", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M %Z", + "%Y-%m-%d %H:%M", + "%Y-%m-%d", + ] + for format in formats: + try: + t = tzparse(dstr, format) + return t + except Exception: + pass + raise Exception("Couldn't parse the date string '%s'" % dstr) + +class ChangeLogEntry: + package = "" + version = "" + logentry = "" + author = "" + email = "" + date = "" + time = "" + +def parse(logfile): + ver_line = "^(\w+) \((\S+)\) (\S+;)? (?:urgency=(\S+))?$" + sig_line = "^ -- ([^<]+) <([^>]+)> (.*?) *$" + + entries = [] + if type(logfile) == type(''): + logfile = open(logfile) + entry = None + for line in logfile: + if re.match(ver_line, line): + package, version, distribution, urgency = re.match(ver_line, line).groups() + entry = ChangeLogEntry() + entry.package = package + entry.version = version + entry.logentry = "" + elif re.match(sig_line, line): + author, email, date = re.match(sig_line, line).groups() + entry.author = author + entry.email = email + entry.date = date + entry.time = parse_date(date) + entry.logentry = entry.logentry.rstrip() + entries += [ entry ] + elif entry: + entry.logentry += line + else: + print "Unexpected line: '%s'" % line + return entries \ No newline at end of file diff --git a/ietf/release/__init__.py b/ietf/release/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ietf/release/models.py b/ietf/release/models.py new file mode 100644 index 000000000..bce5b68f3 --- /dev/null +++ b/ietf/release/models.py @@ -0,0 +1 @@ +# This app has no models diff --git a/ietf/release/tests.py b/ietf/release/tests.py new file mode 100644 index 000000000..501deb776 --- /dev/null +++ b/ietf/release/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/ietf/release/views.py b/ietf/release/views.py new file mode 100644 index 000000000..bbf2e6aa8 --- /dev/null +++ b/ietf/release/views.py @@ -0,0 +1,22 @@ +import re +import os + +from django.template import RequestContext +from django.shortcuts import render_to_response + +import changelog + +def release(request, version=None): + entries = {} + log_entries = changelog.parse("changelog") + next = None + for entry in log_entries: + if next: + next.prev = entry + entry.next = next + next = entry + entries = dict([ (entry.version, entry) for entry in log_entries]) + if version == None: + version = log_entries[0].version + return render_to_response('release/release.html', { 'entry': entries[version], }, context_instance=RequestContext(request)) + diff --git a/ietf/templates/release/release.html b/ietf/templates/release/release.html new file mode 100644 index 000000000..a9289b004 --- /dev/null +++ b/ietf/templates/release/release.html @@ -0,0 +1,23 @@ +{% extends "registration/base.html" %} + +{% block title %}Release information{% endblock %} + +{% block content %} +
+

Release {{ entry.version }}, released {{ entry.date }}

+

+ {% if entry.prev %}← previous release{% else %}← previous release{% endif %} + | + {% if entry.next %}next release →{% else %}next release →{% endif %} +

+

+ Release Notes: + +

+{{entry.logentry}}
+      
+ +

+ +
+{% endblock %} diff --git a/ietf/urls.py b/ietf/urls.py index 3edfa9388..0e12b1889 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -43,33 +43,31 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES: del sitemaps['drafts'] # not needed, overlaps sitemaps['idtracker'] urlpatterns = patterns('', - (r'^feed/(?P.*)/$', 'django.contrib.syndication.views.feed', - { 'feed_dict': feeds}), - (r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', - { 'sitemaps': sitemaps}), - (r'^sitemap-(?P
.+).xml$', 'django.contrib.sitemaps.views.sitemap', - {'sitemaps': sitemaps}), + (r'^$', 'ietf.idrfc.views.main'), + (r'^accounts/', include('ietf.ietfauth.urls')), + (r'^admin/', include(admin.site.urls)), + (r'^admin/doc/', include('django.contrib.admindocs.urls')), (r'^ann/', include('ietf.announcements.urls')), - (r'^idtracker/', include('ietf.idtracker.urls')), + (r'^community/', include('ietf.community.urls')), + (r'^cookies/', include('ietf.cookies.urls')), + (r'^doc/', include('ietf.idrfc.urls')), (r'^drafts/', include('ietf.idindex.urls')), + (r'^feed/(?P.*)/$', 'django.contrib.syndication.views.feed', { 'feed_dict': feeds}), + (r'^idtracker/', include('ietf.idtracker.urls')), (r'^iesg/', include('ietf.iesg.urls')), + (r'^ipr/', include('ietf.ipr.urls')), (r'^liaison/', include('ietf.liaisons.urls')), (r'^list/', include('ietf.mailinglists.urls')), - (r'^(?Ppublic)/', include('ietf.redirects.urls')), - (r'^ipr/', include('ietf.ipr.urls')), (r'^meeting/', include('ietf.meeting.urls')), - (r'^accounts/', include('ietf.ietfauth.urls')), - (r'^doc/', include('ietf.idrfc.urls')), - (r'^wg/', include('ietf.wginfo.urls')), - (r'^cookies/', include('ietf.cookies.urls')), (r'^person/', include('ietf.person.urls')), - (r'^submit/', include('ietf.submit.urls')), + (r'^release/$', 'ietf.release.views.release'), + (r'^release/(?P.+)/$', 'ietf.release.views.release'), + (r'^sitemap-(?P
.+).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}), + (r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', { 'sitemaps': sitemaps}), (r'^streams/', include('ietf.ietfworkflows.urls')), - (r'^community/', include('ietf.community.urls')), - - (r'^$', 'ietf.idrfc.views.main'), - (r'^admin/doc/', include('django.contrib.admindocs.urls')), - ('^admin/', include(admin.site.urls)), + (r'^submit/', include('ietf.submit.urls')), + (r'^(?Ppublic)/', include('ietf.redirects.urls')), + (r'^wg/', include('ietf.wginfo.urls')), # Google webmaster tools verification url (r'^googlea30ad1dacffb5e5b.html', 'django.views.generic.simple.direct_to_template', { 'template': 'googlea30ad1dacffb5e5b.html' }),