[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('-', '&ndash;')
@@ -180,10 +196,27 @@ def refresh_page(target, database):
     else:
         resolved = ''
 
+    issues = []
+    for kind, message in database.list_issues():
+      if kind is None:
+        kind = '&nbsp;'
+        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('-', '&ndash;')
     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