feeds.py :  » Build » Buildbot » buildbot-0.8.0 » buildbot » status » web » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Build » Buildbot 
Buildbot » buildbot 0.8.0 » buildbot » status » web » feeds.py
# This module enables ATOM and RSS feeds from webstatus.
#
# It is based on "feeder.py" which was part of the Buildbot
# configuration for the Subversion project. The original file was
# created by Lieven Gobaerts and later adjusted by API
# (apinheiro@igalia.coma) and also here
# http://code.google.com/p/pybots/source/browse/trunk/master/Feeder.py
#
# All subsequent changes to feeder.py where made by Chandan-Dutta
# Chowdhury <chandan-dutta.chowdhury @ hp.com> and Gareth Armstrong
# <gareth.armstrong @ hp.com>.
#
# Those modifications are as follows:
# 1) the feeds are usable from baseweb.WebStatus
# 2) feeds are fully validated ATOM 1.0 and RSS 2.0 feeds, verified
#    with code from http://feedvalidator.org
# 3) nicer xml output
# 4) feeds can be filtered as per the /waterfall display with the
#    builder and category filters
# 5) cleaned up white space and imports
#
# Finally, the code was directly integrated into these two files,
# buildbot/status/web/feeds.py (you're reading it, ;-)) and
# buildbot/status/web/baseweb.py.

import os
import re
import time
from twisted.web import resource
from buildbot.status.builder import FAILURE

class XmlResource(resource.Resource):
    contentType = "text/xml; charset=UTF-8"
    docType = ''

    def getChild(self, name, request):
        return self

    def render(self, request):
        data = self.content(request)
        request.setHeader("content-type", self.contentType)
        if request.method == "HEAD":
            request.setHeader("content-length", len(data))
            return ''
        return data

class FeedResource(XmlResource):
    title = None
    link = 'http://dummylink'
    language = 'en-us'
    description = 'Dummy rss'
    status = None

    def __init__(self, status, categories=None, title=None):
        self.status = status
        self.categories = categories
        self.title = title
        self.projectName = self.status.getProjectName()
        self.link = self.status.getBuildbotURL()
        self.description = 'List of builds'
        self.pubdate = time.gmtime(int(time.time()))
        self.user = self.getEnv(['USER', 'USERNAME'], 'buildmaster')
        self.hostname = self.getEnv(['HOSTNAME', 'COMPUTERNAME'],
                                    'buildmaster')
        self.children = {}

    def getEnv(self, keys, fallback):
        for key in keys:
            if key in os.environ:
                return os.environ[key]
        return fallback

    def getBuilds(self, request):
        builds = []
        # THIS is lifted straight from the WaterfallStatusResource Class in
        # status/web/waterfall.py
        #
        # we start with all Builders available to this Waterfall: this is
        # limited by the config-file -time categories= argument, and defaults
        # to all defined Builders.
        allBuilderNames = self.status.getBuilderNames(categories=self.categories)
        builders = [self.status.getBuilder(name) for name in allBuilderNames]

        # but if the URL has one or more builder= arguments (or the old show=
        # argument, which is still accepted for backwards compatibility), we
        # use that set of builders instead. We still don't show anything
        # outside the config-file time set limited by categories=.
        showBuilders = request.args.get("show", [])
        showBuilders.extend(request.args.get("builder", []))
        if showBuilders:
            builders = [b for b in builders if b.name in showBuilders]

        # now, if the URL has one or category= arguments, use them as a
        # filter: only show those builders which belong to one of the given
        # categories.
        showCategories = request.args.get("category", [])
        if showCategories:
            builders = [b for b in builders if b.category in showCategories]

        failures_only = request.args.get("failures_only", "false")

        maxFeeds = 25

        # Copy all failed builds in a new list.
        # This could clearly be implemented much better if we had
        # access to a global list of builds.
        for b in builders:
            lastbuild = b.getLastFinishedBuild()
            if lastbuild is None:
                continue

            lastnr = lastbuild.getNumber()

            totalbuilds = 0
            i = lastnr
            while i >= 0:
                build = b.getBuild(i)
                i -= 1
                if not build:
                    continue

                results = build.getResults()

                if failures_only == "false" or results == FAILURE:
                    totalbuilds += 1
                    builds.append(build)

                # stop for this builder when our total nr. of feeds is reached
                if totalbuilds >= maxFeeds:
                    break

        # Sort build list by date, youngest first.
        # To keep compatibility with python < 2.4, use this for sorting instead:
        # We apply Decorate-Sort-Undecorate
        deco = [(build.getTimes(), build) for build in builds]
        deco.sort()
        deco.reverse()
        builds = [build for (b1, build) in deco]

        if builds:
            builds = builds[:min(len(builds), maxFeeds)]
        return builds

    def content(self, request):
        builds = self.getBuilds(request)

        build_cxts = []

        for build in builds:
            start, finished = build.getTimes()
            finishedTime = time.gmtime(int(finished))
            link = re.sub(r'index.html', "", self.status.getURLForThing(build))

            # title: trunk r22191 (plus patch) failed on
            # 'i686-debian-sarge1 shared gcc-3.3.5'
            ss = build.getSourceStamp()
            source = ""
            if ss.branch:
                source += "Branch %s " % ss.branch
            if ss.revision:
                source += "Revision %s " % str(ss.revision)
            if ss.patch:
                source += " (plus patch)"
            if ss.changes:
                pass
            if (ss.branch is None and ss.revision is None and ss.patch is None
                and not ss.changes):
                source += "Latest revision "
            got_revision = None
            try:
                got_revision = build.getProperty("got_revision")
            except KeyError:
                pass
            if got_revision:
                got_revision = str(got_revision)
                if len(got_revision) > 40:
                    got_revision = "[revision string too long]"
                source += "(Got Revision: %s)" % got_revision
            failflag = (build.getResults() != FAILURE)
            title = ('%s %s on "%s"' %
                     (source, ["failed","succeeded"][failflag],
                      build.getBuilder().getName()))

            # Add information about the failing steps.
            failed_steps = []
            log_lines = []
            for s in build.getSteps():
                if s.getResults()[0] == FAILURE:
                    failed_steps.append(s.getName())

                    # Add the last 30 lines of each log.
                    for log in s.getLogs():
                        log_lines.append('Last lines of build log "%s":' %
                                         log.getName())
                        log_lines.append([])
                        try:
                            logdata = log.getText()
                        except IOError:
                            # Probably the log file has been removed
                            logdata ='** log file not available **'

                        log_lines.extend(logdata.split('\n')[-30:])

            bc = {}
            bc['date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                       finishedTime)
            bc['summary_link'] = ('%sbuilders/%s' %
                                  (self.link,
                                   build.getBuilder().getName()))            
            bc['name'] = build.getBuilder().getName()
            bc['number'] = build.getNumber()
            bc['responsible_users'] = build.getResponsibleUsers()
            bc['failed_steps'] = failed_steps
            bc['title'] = title
            bc['link'] = link
            bc['log_lines'] = log_lines

            if finishedTime is not None:
                bc['rfc822_pubdate'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                              finishedTime)
                bc['rfc3339_pubdate'] = time.strftime("%Y-%m-%dT%H:%M:%SZ",
                                               finishedTime)

                # Every RSS/Atom item must have a globally unique ID
                guid = ('tag:%s@%s,%s:%s' %
                        (self.user, self.hostname,
                         time.strftime("%Y-%m-%d", finishedTime),
                         time.strftime("%Y%m%d%H%M%S", finishedTime)))
                bc['guid'] = guid

            build_cxts.append(bc)

        title = self.title
        if not title:
            title = 'Build status of %s' % self.projectName

        cxt = {}
        cxt['title'] = title
        cxt['project_url'] = self.link
        cxt['project_name'] = self.projectName
        cxt['language'] = self.language
        cxt['description'] = self.description
        if self.pubdate is not None:
            cxt['rfc822_pubdate'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
                                                  self.pubdate)
            cxt['rfc3339_pubdate'] = time.strftime("%Y-%m-%dT%H:%M:%SZ",
                                                   self.pubdate)

        cxt['builds'] = build_cxts
        template = request.site.buildbot_service.templates.get_template(self.template_file)
        return template.render(**cxt).encode('utf-8').strip()

class Rss20StatusResource(FeedResource):
    # contentType = 'application/rss+xml' (browser dependent)
    template_file = 'feed_rss20.xml'

    def __init__(self, status, categories=None, title=None):
        FeedResource.__init__(self, status, categories, title)

class Atom10StatusResource(FeedResource):
    # contentType = 'application/atom+xml' (browser dependent)
    template_file = 'feed_atom10.xml'

    def __init__(self, status, categories=None, title=None):
        FeedResource.__init__(self, status, categories, title)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.