[prev in list] [next in list] [prev in thread] [next in thread]
List: incubator-cvs
Subject: svn commit: r1440666 - in /incubator/public/trunk/voter: podlings.py status.py utility.py voter.py
From: brane () apache ! org
Date: 2013-01-30 20:55:41
Message-ID: 20130130205541.5CF382388A56 () eris ! apache ! org
[Download RAW message or body]
Author: brane
Date: Wed Jan 30 20:55:40 2013
New Revision: 1440666
URL: http://svn.apache.org/viewvc?rev=1440666&view=rev
Log:
Record issues found during votes processing in the database and on the generated \
status page.
Modified:
incubator/public/trunk/voter/podlings.py
incubator/public/trunk/voter/status.py
incubator/public/trunk/voter/utility.py
incubator/public/trunk/voter/voter.py
Modified: incubator/public/trunk/voter/podlings.py
URL: http://svn.apache.org/viewvc/incubator/public/trunk/voter/podlings.py?rev=1440666&r1=1440665&r2=1440666&view=diff
==============================================================================
--- incubator/public/trunk/voter/podlings.py (original)
+++ incubator/public/trunk/voter/podlings.py Wed Jan 30 20:55:40 2013
@@ -28,12 +28,45 @@ import traceback
import xml.etree.ElementTree
sys.path.insert(0, os.path.dirname(__file__))
-from utility import SiteStructure, UTC
+from utility import Issue, SiteStructure, UTC
Podling = collections.namedtuple('Podling', ('name', 'mbox_relpath'))
-def __formatted_exception():
- return ''.join(traceback.format_exception_only(*sys.exc_info()[:2]))
+class Issues(object):
+ ERROR = 'ERROR'
+ WARNING = 'WARNING'
+
+ def __init__(self):
+ self.__issues = []
+
+ def __exfmt():
+ return ''.join(traceback.format_exception_only(*sys.exc_info()[:2]))
+
+ def __writelast(self):
+ issue = self.__issues[-1]
+ if issue.kind:
+ sys.stderr.write('%s: %s\n' % issue)
+ else:
+ sys.stderr.write(issue.message)
+
+ @property
+ def issues(self):
+ return self.__issues[:]
+
+ def record_warning(self, message):
+ self.__issues.append(Issue(self.WARNING, message))
+ self.__writelast()
+
+ def record_error(self, message):
+ self.__issues.append(Issue(self.ERROR, message))
+ self.__writelast()
+
+ def record_exception(self, message):
+ self.record_error(message)
+ self.__issues.append(Issue(None, self.__exfmt()))
+ self.__writelast()
+__issues = Issues()
+
def __parse_xml_file(path):
"""
@@ -43,8 +76,7 @@ def __parse_xml_file(path):
try:
return xml.etree.ElementTree.parse(path)
except:
- sys.stderr.write('ERROR: Could not parse ' + path + '\n')
- sys.stderr.write(__formatted_exception())
+ __issues.record_exception('Could not parse ' + path)
return None
def __find_podling_names():
@@ -77,14 +109,16 @@ def __relpath_from_email(email):
# Some podlings are silly and add crud to the mailing list address
offset = email.find(__incubator)
if offset < 0:
- sys.stderr.write('WARNING: Not an Incubator address: ' + email + '\n')
+ __issues.record_warning('Not an Incubator address: ' + email)
return None
+ if email[offset + len(__incubator):]:
+ __issues.record_warning('Ignoring crud following address: ' + email)
email = email[:offset + len(__incubator)]
# Split the email into list name and host name
list_name, atsign, host = email.partition('@')
if not atsign or not host:
- sys.stderr.write('WARNING: Invalid mail address: ' + email + '\n')
+ __issues.record_warning('Invalid mail address: ' + email)
return None
# Old-style podling lists are inside the incubator archives.
@@ -106,19 +140,19 @@ def podling_archives():
continue
info = doc.find('.//*[@id="mail-dev"]')
if info is None:
- sys.stderr.write('WARNING: ' + name + ' has no dev list?\n')
+ __issues.record_warning(name + ' has no dev list?')
continue
email = ''
for el in info.iter():
email += (el.text or '')
podlings.append(Podling(name, __relpath_from_email(email)))
- return podlings
+ return podlings, __issues
except:
- sys.stderr.write('ERROR: Could not find podlings\n')
- sys.stderr.write(__formatted_exception())
- return []
+ __issues.record_exception('Could not find any podlings')
+ return [], __issues
def test():
- for p in podling_archives:
+ podlings, issues = podling_archives()
+ for p in podlings:
print('%25s %s' % (p.name + ' dev list:', p.mbox_relpath))
Modified: incubator/public/trunk/voter/status.py
URL: http://svn.apache.org/viewvc/incubator/public/trunk/voter/status.py?rev=1440666&r1=1440665&r2=1440666&view=diff
==============================================================================
--- incubator/public/trunk/voter/status.py (original)
+++ incubator/public/trunk/voter/status.py Wed Jan 30 20:55:40 2013
@@ -81,6 +81,7 @@ __page_template = """\
<p>Last recorded change: %s</p>
%s
%s
+%s
</body>
</html>
"""
@@ -125,6 +126,21 @@ __closed_row = """\
<td>%(closed)s</td>
</tr>"""
+__issues_table = """\
+ <h2>Problems Found</h2>
+ <table>
+ <tr>
+ <th>Severity</th>
+ <th>Description</th>
+ </tr>
+%s
+ </table>"""
+
+__issue_row = """\
+ <tr class="%(klass)s">
+ <td>%(kind)s</td>
+ <td>%(message)s</td>
+ </tr>"""
def escape_date(dateobject):
return cgi.escape(UTC.timedate(dateobject)).replace('-', '–')
@@ -180,10 +196,27 @@ def refresh_page(target, database):
else:
resolved = ''
+ issues = []
+ for kind, message in database.list_issues():
+ if kind is None:
+ kind = ' '
+ klass = 'normal'
+ elif kind == 'WARNING':
+ klass = 'nudge'
+ else:
+ klass = 'yell'
+ issues.append(__issue_row % dict(klass = klass,
+ kind = kind,
+ message = cgi.escape(message)))
+ if issues:
+ issues = __issues_table % '\n'.join(issues)
+ else:
+ issues = ''
+
updated = cgi.escape(UTC.timestring(database.updated)).replace('-', '–')
temp = target + '.temp'
with open(temp, 'wt') as page:
- page.write(__page_template % (updated, current, resolved))
+ page.write(__page_template % (updated, current, resolved, issues))
os.rename(temp, target)
Modified: incubator/public/trunk/voter/utility.py
URL: http://svn.apache.org/viewvc/incubator/public/trunk/voter/utility.py?rev=1440666&r1=1440665&r2=1440666&view=diff
==============================================================================
--- incubator/public/trunk/voter/utility.py (original)
+++ incubator/public/trunk/voter/utility.py Wed Jan 30 20:55:40 2013
@@ -24,9 +24,12 @@ Status: Alpha
from __future__ import absolute_import
import os.path
+import collections
import datetime
+Issue = collections.namedtuple('Issue', ('kind', 'message'))
+
class __UTC(datetime.tzinfo):
"""
A datetime.tzinfo object representing Universal Coordinated Time
@@ -95,3 +98,4 @@ class SiteStructure(object):
@classmethod
def podlings(cls):
return cls.content_path('podlings.xml')
+
Modified: incubator/public/trunk/voter/voter.py
URL: http://svn.apache.org/viewvc/incubator/public/trunk/voter/voter.py?rev=1440666&r1=1440665&r2=1440666&view=diff
==============================================================================
--- incubator/public/trunk/voter/voter.py (original)
+++ incubator/public/trunk/voter/voter.py Wed Jan 30 20:55:40 2013
@@ -43,7 +43,7 @@ import email.utils
import sqlite3
sys.path.insert(0, os.path.dirname(__file__))
-from utility import SiteStructure, UTC
+from utility import Issue, SiteStructure, UTC
from podlings import Podling, podling_archives
@@ -128,22 +128,35 @@ class VoteUpdater(object):
self.mbox_basedir = mbox_archive_basedir
self.mbox_relpaths = []
- archives = ([Podling('Incubator', 'incubator.apache.org/general')]
- + podling_archives())
- month = datetime.datetime.utcnow().strftime('%Y%m')
+ archives, self.issues = podling_archives()
+
+ archives.insert(0, Podling('Incubator', 'incubator.apache.org/general'))
+
+ now = datetime.datetime.utcnow()
+ thismonth = datetime.datetime(now.year, now.month, 1)
+ if now.month == 1:
+ lastmonth = datetime.datetime(now.year - 1, now.month, 1)
+ else:
+ lastmonth = datetime.datetime(now.year, now.month - 1, 1)
+
+ current = thismonth.strftime('%Y%m')
+ previous = lastmonth.strftime('%Y%m') + '.gz'
for archive in archives:
if not archive.mbox_relpath:
continue
basedir = os.path.join(mbox_archive_basedir, archive.mbox_relpath)
if not os.path.isdir(basedir):
- sys.stderr.write('WARNING: ' + archive.name
- + ' has no archive at ' + basedir + '\n')
+ self.issues.record_warning(
+ archive.name + ' has no archive at ' + basedir)
continue
- if not os.path.isfile(os.path.join(basedir, month)):
- # Skip archives that have no mbox file for the current month.
- continue
- self.mbox_relpaths.append(os.path.join(archive.mbox_relpath, month))
+
+ if os.path.isfile(os.path.join(basedir, current)):
+ self.mbox_relpaths.append(
+ os.path.join(archive.mbox_relpath, current))
+ if os.path.isfile(os.path.join(basedir, previous)):
+ self.mbox_relpaths.append(
+ os.path.join(archive.mbox_relpath, previous))
ParsedVote = collections.namedtuple(
'ParsedVote',
@@ -152,6 +165,10 @@ class VoteUpdater(object):
ParsedMBox = collections.namedtuple('ParsedMBox', ('relpath', 'mtime'))
def __parse_mbox(self, mbox_path, mtime, votes):
+ # TODO: Parse compressed mboxes
+ if mbox_path.endswith('.gz'):
+ return
+
mbox = MBoxParser.parse(mbox_path, mtime)
for e in mbox.entries:
parsed = self.__subject_rx.match(e.title)
@@ -197,6 +214,7 @@ class VoteUpdater(object):
cancelled = v.cancelled)
for v in sorted(votes)),
(self.ParsedMBox(r, m) for p, r, m in mboxes))
+ database.record_issues(self.issues.issues)
class VoteDatabase(object):
@@ -205,7 +223,6 @@ class VoteDatabase(object):
"""
__schema = """
- DROP TABLE IF EXISTS feedinfo;
CREATE TABLE feedinfo (
rowid INTEGER NOT NULL PRIMARY KEY,
updated TEXT NOT NULL,
@@ -213,7 +230,12 @@ class VoteDatabase(object):
);
INSERT INTO feedinfo (rowid, updated) VALUES (1, '');
- DROP TABLE IF EXISTS vote;
+ CREATE TABLE issue (
+ rowid INTEGER NOT NULL PRIMARY KEY,
+ kind TEXT DEFAULT NULL,
+ message TEXT NOT NULL
+ );
+
CREATE TABLE vote (
sortkey TEXT NOT NULL PRIMARY KEY,
subject TEXT NOT NULL,
@@ -225,7 +247,6 @@ class VoteDatabase(object):
CREATE INDEX updated_index ON vote(updated DESC);
CREATE INDEX closed_index ON vote(closed DESC);
- DROP TABLE IF EXISTS mbox;
CREATE TABLE mbox (
relpath TEXT NOT NULL PRIMARY KEY,
mtime FLOAT NOT NULL
@@ -378,6 +399,18 @@ class VoteDatabase(object):
txn.con.execute("UPDATE feedinfo SET updated = ?",
(UTC.timestring(updated),))
txn.__updated = None
+ pass
+
+ def record_issues(self, issues):
+ with self.transaction() as txn:
+ cursor = txn.con.cursor()
+ cursor.execute("DELETE FROM issue");
+ if not issues:
+ return
+ for issue in issues:
+ cursor.execute("INSERT INTO issue (kind, message)"
+ " VALUES (?, ?)",
+ issue)
def __list_votes(self, active):
if active:
@@ -401,6 +434,13 @@ class VoteDatabase(object):
def list_resolved_votes(self):
return self.__list_votes(False)
+ def list_issues(self):
+ cursor = self.con.cursor()
+ cursor.execute("SELECT kind, message FROM issue"
+ " ORDER BY rowid ASC")
+ for row in cursor.fetchall():
+ yield(Issue(**row))
+
def prune_old_votes(self):
now = datetime.datetime.utcnow()
cursor = self.con.cursor()
---------------------------------------------------------------------
To unsubscribe, e-mail: cvs-unsubscribe@incubator.apache.org
For additional commands, e-mail: cvs-help@incubator.apache.org
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic