BuildNotify-0.3.5/0000755000175000017500000000000012315063430013646 5ustar anayanay00000000000000BuildNotify-0.3.5/buildnotifyapplet.py0000755000175000017500000000011212315063423017755 0ustar anayanay00000000000000#!/usr/bin/env python import buildnotifylib buildnotifylib.BuildNotify() BuildNotify-0.3.5/setup.py0000644000175000017500000000155112315063423015364 0ustar anayanay00000000000000#!/usr/bin/env python from setuptools import setup setup (name='BuildNotify', version="0.3.5", description='Cruise Control build monitor for Windows/Linux/Mac', keywords='cctray ccmenu buildnotify ubuntu linux cruisecontrol continuous integration ci', author='Anay Nayak', requires = ['pytz'], author_email='anayak007@gmail.com', url = "http://bitbucket.org/Anay/buildnotify/", license='GPL v3', long_description = 'BuildNotify is a cruise control system tray monitor which works on Windows/Linux/Mac.' + 'It was largely inspired from CCMenu and lets you monitor multiple continuous integration servers with' + 'customizable build notifications for all projects', packages=['buildnotifylib', 'buildnotifylib.core', 'buildnotifylib.generated'], scripts = ['buildnotifyapplet.py']) BuildNotify-0.3.5/BuildNotify.egg-info/0000755000175000017500000000000012315063430017570 5ustar anayanay00000000000000BuildNotify-0.3.5/BuildNotify.egg-info/top_level.txt0000664000175000017500000000001712315063430022322 0ustar anayanay00000000000000buildnotifylib BuildNotify-0.3.5/BuildNotify.egg-info/dependency_links.txt0000664000175000017500000000000112315063430023640 0ustar anayanay00000000000000 BuildNotify-0.3.5/BuildNotify.egg-info/PKG-INFO0000664000175000017500000000113512315063430020667 0ustar anayanay00000000000000Metadata-Version: 1.1 Name: BuildNotify Version: 0.3.5 Summary: Cruise Control build monitor for Windows/Linux/Mac Home-page: http://bitbucket.org/Anay/buildnotify/ Author: Anay Nayak Author-email: anayak007@gmail.com License: GPL v3 Description: BuildNotify is a cruise control system tray monitor which works on Windows/Linux/Mac.It was largely inspired from CCMenu and lets you monitor multiple continuous integration servers withcustomizable build notifications for all projects Keywords: cctray ccmenu buildnotify ubuntu linux cruisecontrol continuous integration ci Platform: UNKNOWN Requires: pytz BuildNotify-0.3.5/BuildNotify.egg-info/SOURCES.txt0000664000175000017500000000162212315063430021457 0ustar anayanay00000000000000MANIFEST.in README buildnotifyapplet.py setup.py BuildNotify.egg-info/PKG-INFO BuildNotify.egg-info/SOURCES.txt BuildNotify.egg-info/dependency_links.txt BuildNotify.egg-info/top_level.txt buildnotifylib/__init__.py buildnotifylib/app_menu.py buildnotifylib/app_notification.py buildnotifylib/app_ui.py buildnotifylib/build_icons.py buildnotifylib/buildnotify.py buildnotifylib/config.py buildnotifylib/notifications.py buildnotifylib/preferences.py buildnotifylib/project_status_notification.py buildnotifylib/server_configuration_dialog.py buildnotifylib/version.py buildnotifylib/core/__init__.py buildnotifylib/core/distance_of_time.py buildnotifylib/core/http_connection.py buildnotifylib/core/projects.py buildnotifylib/core/timed_event.py buildnotifylib/generated/__init__.py buildnotifylib/generated/icons_rc.py buildnotifylib/generated/preferences_ui.py buildnotifylib/generated/server_configuration_ui.pyBuildNotify-0.3.5/PKG-INFO0000664000175000017500000000113512315063430014745 0ustar anayanay00000000000000Metadata-Version: 1.1 Name: BuildNotify Version: 0.3.5 Summary: Cruise Control build monitor for Windows/Linux/Mac Home-page: http://bitbucket.org/Anay/buildnotify/ Author: Anay Nayak Author-email: anayak007@gmail.com License: GPL v3 Description: BuildNotify is a cruise control system tray monitor which works on Windows/Linux/Mac.It was largely inspired from CCMenu and lets you monitor multiple continuous integration servers withcustomizable build notifications for all projects Keywords: cctray ccmenu buildnotify ubuntu linux cruisecontrol continuous integration ci Platform: UNKNOWN Requires: pytz BuildNotify-0.3.5/setup.cfg0000664000175000017500000000007312315063430015471 0ustar anayanay00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 BuildNotify-0.3.5/buildnotifylib/0000755000175000017500000000000012315063430016665 5ustar anayanay00000000000000BuildNotify-0.3.5/buildnotifylib/generated/0000755000175000017500000000000012315063430020623 5ustar anayanay00000000000000BuildNotify-0.3.5/buildnotifylib/generated/server_configuration_ui.py0000644000175000017500000001270712315063423026140 0ustar anayanay00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'data/server_configuration.ui' # # Created: Tue Mar 18 20:49:08 2014 # by: PyQt4 UI code generator 4.9.3 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: _fromUtf8 = lambda s: s class Ui_serverConfigurationDialog(object): def setupUi(self, serverConfigurationDialog): serverConfigurationDialog.setObjectName(_fromUtf8("serverConfigurationDialog")) serverConfigurationDialog.resize(440, 381) self.projectsList = QtGui.QListView(serverConfigurationDialog) self.projectsList.setGeometry(QtCore.QRect(20, 80, 401, 191)) self.projectsList.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.projectsList.setSelectionMode(QtGui.QAbstractItemView.NoSelection) self.projectsList.setObjectName(_fromUtf8("projectsList")) self.label = QtGui.QLabel(serverConfigurationDialog) self.label.setGeometry(QtCore.QRect(20, 60, 391, 20)) self.label.setObjectName(_fromUtf8("label")) self.cctrayUrlLabel = QtGui.QLabel(serverConfigurationDialog) self.cctrayUrlLabel.setGeometry(QtCore.QRect(20, 0, 161, 41)) self.cctrayUrlLabel.setObjectName(_fromUtf8("cctrayUrlLabel")) self.addServerUrl = QtGui.QLineEdit(serverConfigurationDialog) self.addServerUrl.setGeometry(QtCore.QRect(20, 30, 311, 30)) self.addServerUrl.setObjectName(_fromUtf8("addServerUrl")) self.loadUrlButton = QtGui.QPushButton(serverConfigurationDialog) self.loadUrlButton.setGeometry(QtCore.QRect(340, 30, 81, 31)) self.loadUrlButton.setAutoDefault(False) self.loadUrlButton.setObjectName(_fromUtf8("loadUrlButton")) self.submitButton = QtGui.QPushButton(serverConfigurationDialog) self.submitButton.setGeometry(QtCore.QRect(340, 340, 81, 31)) self.submitButton.setAutoDefault(False) self.submitButton.setObjectName(_fromUtf8("submitButton")) self.layoutWidget = QtGui.QWidget(serverConfigurationDialog) self.layoutWidget.setGeometry(QtCore.QRect(20, 280, 401, 41)) self.layoutWidget.setObjectName(_fromUtf8("layoutWidget")) self.horizontalLayout = QtGui.QHBoxLayout(self.layoutWidget) self.horizontalLayout.setSizeConstraint(QtGui.QLayout.SetFixedSize) self.horizontalLayout.setMargin(0) self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout")) self.timezoneLabel = QtGui.QLabel(self.layoutWidget) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.timezoneLabel.sizePolicy().hasHeightForWidth()) self.timezoneLabel.setSizePolicy(sizePolicy) self.timezoneLabel.setMinimumSize(QtCore.QSize(180, 0)) self.timezoneLabel.setMaximumSize(QtCore.QSize(180, 16777215)) self.timezoneLabel.setObjectName(_fromUtf8("timezoneLabel")) self.horizontalLayout.addWidget(self.timezoneLabel) self.timezoneList = QtGui.QComboBox(self.layoutWidget) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.timezoneList.sizePolicy().hasHeightForWidth()) self.timezoneList.setSizePolicy(sizePolicy) self.timezoneList.setMinimumSize(QtCore.QSize(210, 0)) self.timezoneList.setMaximumSize(QtCore.QSize(200, 16777215)) self.timezoneList.setObjectName(_fromUtf8("timezoneList")) self.horizontalLayout.addWidget(self.timezoneList) self.label.setBuddy(self.projectsList) self.cctrayUrlLabel.setBuddy(self.addServerUrl) self.timezoneLabel.setBuddy(self.timezoneList) self.retranslateUi(serverConfigurationDialog) QtCore.QObject.connect(self.addServerUrl, QtCore.SIGNAL(_fromUtf8("returnPressed()")), self.loadUrlButton.click) QtCore.QObject.connect(self.submitButton, QtCore.SIGNAL(_fromUtf8("clicked()")), serverConfigurationDialog.accept) QtCore.QMetaObject.connectSlotsByName(serverConfigurationDialog) serverConfigurationDialog.setTabOrder(self.addServerUrl, self.loadUrlButton) serverConfigurationDialog.setTabOrder(self.loadUrlButton, self.submitButton) serverConfigurationDialog.setTabOrder(self.submitButton, self.projectsList) def retranslateUi(self, serverConfigurationDialog): serverConfigurationDialog.setWindowTitle(QtGui.QApplication.translate("serverConfigurationDialog", "Add Server", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("serverConfigurationDialog", "Select projects", None, QtGui.QApplication.UnicodeUTF8)) self.cctrayUrlLabel.setText(QtGui.QApplication.translate("serverConfigurationDialog", "Path to cctray.xml", None, QtGui.QApplication.UnicodeUTF8)) self.loadUrlButton.setText(QtGui.QApplication.translate("serverConfigurationDialog", "Load", None, QtGui.QApplication.UnicodeUTF8)) self.submitButton.setText(QtGui.QApplication.translate("serverConfigurationDialog", "OK", None, QtGui.QApplication.UnicodeUTF8)) self.timezoneLabel.setText(QtGui.QApplication.translate("serverConfigurationDialog", "Server timezone", None, QtGui.QApplication.UnicodeUTF8)) BuildNotify-0.3.5/buildnotifylib/generated/icons_rc.py0000644000175000017500000001505312315063423023002 0ustar anayanay00000000000000# -*- coding: utf-8 -*- # Resource object code # # Created: Tue Mar 18 20:49:08 2014 # by: The Resource Compiler for PyQt (Qt v4.8.2) # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore qt_resource_data = "\ \x00\x00\x00\xcd\ \x3c\ \x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ \x30\x2f\x73\x76\x67\x22\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\ \x31\x2e\x31\x22\x3e\x0a\x20\x20\x3c\x72\x65\x63\x74\x20\x78\x3d\ \x22\x30\x22\x20\x79\x3d\x22\x30\x22\x20\x72\x78\x3d\x22\x31\x35\ \x30\x22\x20\x72\x79\x3d\x22\x31\x35\x30\x22\x20\x77\x69\x64\x74\ \x68\x3d\x22\x37\x34\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ \x37\x34\x34\x22\x0a\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ \x6c\x6c\x3a\x72\x67\x62\x28\x31\x34\x36\x2c\x32\x30\x38\x2c\x38\ \x30\x29\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\x61\x63\x6b\x3b\ \x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\ \x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x2e\ \x35\x22\x2f\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ \x00\x00\x00\xcd\ \x3c\ \x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ \x30\x2f\x73\x76\x67\x22\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\ \x31\x2e\x31\x22\x3e\x0a\x20\x20\x3c\x72\x65\x63\x74\x20\x78\x3d\ \x22\x30\x22\x20\x79\x3d\x22\x30\x22\x20\x72\x78\x3d\x22\x31\x35\ \x30\x22\x20\x72\x79\x3d\x22\x31\x35\x30\x22\x20\x77\x69\x64\x74\ \x68\x3d\x22\x37\x34\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ \x37\x34\x34\x22\x0a\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ \x6c\x6c\x3a\x72\x67\x62\x28\x31\x34\x36\x2c\x32\x30\x38\x2c\x38\ \x30\x29\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\x61\x63\x6b\x3b\ \x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\ \x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x2e\ \x35\x22\x2f\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ \x00\x00\x00\xce\ \x3c\ \x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ \x30\x2f\x73\x76\x67\x22\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\ \x31\x2e\x31\x22\x3e\x0a\x20\x20\x3c\x72\x65\x63\x74\x20\x78\x3d\ \x22\x30\x22\x20\x79\x3d\x22\x30\x22\x20\x72\x78\x3d\x22\x31\x35\ \x30\x22\x20\x72\x79\x3d\x22\x31\x35\x30\x22\x20\x77\x69\x64\x74\ \x68\x3d\x22\x37\x34\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ \x37\x34\x34\x22\x0a\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ \x6c\x6c\x3a\x72\x67\x62\x28\x31\x30\x32\x2c\x31\x30\x32\x2c\x31\ \x30\x32\x29\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\x61\x63\x6b\ \x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\ \x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\ \x2e\x35\x22\x2f\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ \x00\x00\x00\xcc\ \x3c\ \x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ \x30\x2f\x73\x76\x67\x22\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\ \x31\x2e\x31\x22\x3e\x0a\x20\x20\x3c\x72\x65\x63\x74\x20\x78\x3d\ \x22\x30\x22\x20\x79\x3d\x22\x30\x22\x20\x72\x78\x3d\x22\x31\x35\ \x30\x22\x20\x72\x79\x3d\x22\x31\x35\x30\x22\x20\x77\x69\x64\x74\ \x68\x3d\x22\x37\x34\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ \x37\x34\x34\x22\x0a\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ \x6c\x6c\x3a\x72\x67\x62\x28\x32\x35\x35\x2c\x31\x35\x33\x2c\x30\ \x29\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\x61\x63\x6b\x3b\x73\ \x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x35\x3b\x73\x74\ \x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x30\x2e\x35\ \x22\x2f\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ \x00\x00\x00\xc1\ \x3c\ \x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\ \x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\ \x30\x2f\x73\x76\x67\x22\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\ \x31\x2e\x31\x22\x3e\x0a\x20\x20\x3c\x72\x65\x63\x74\x20\x78\x3d\ \x22\x30\x22\x20\x79\x3d\x22\x30\x22\x20\x72\x78\x3d\x22\x31\x35\ \x30\x22\x20\x72\x79\x3d\x22\x31\x35\x30\x22\x20\x77\x69\x64\x74\ \x68\x3d\x22\x37\x34\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\ \x37\x34\x34\x22\x0a\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x66\x69\ \x6c\x6c\x3a\x72\x65\x64\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x62\x6c\ \x61\x63\x6b\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\ \x3a\x35\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\x74\ \x79\x3a\x30\x2e\x35\x22\x2f\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\ \ " qt_resource_name = "\ \x00\x06\ \x07\xaa\x8b\xc3\ \x00\x73\ \x00\x74\x00\x61\x00\x74\x00\x75\x00\x73\ \x00\x05\ \x00\x6f\xa6\x53\ \x00\x69\ \x00\x63\x00\x6f\x00\x6e\x00\x73\ \x00\x17\ \x02\x27\x97\xc7\ \x00\x62\ \x00\x75\x00\x69\x00\x6c\x00\x64\x00\x6e\x00\x6f\x00\x74\x00\x69\x00\x66\x00\x79\x00\x2d\x00\x73\x00\x75\x00\x63\x00\x63\x00\x65\ \x00\x73\x00\x73\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x20\ \x02\xe1\xd6\x67\ \x00\x62\ \x00\x75\x00\x69\x00\x6c\x00\x64\x00\x6e\x00\x6f\x00\x74\x00\x69\x00\x66\x00\x79\x00\x2d\x00\x73\x00\x75\x00\x63\x00\x63\x00\x65\ \x00\x73\x00\x73\x00\x2d\x00\x62\x00\x75\x00\x69\x00\x6c\x00\x64\x00\x69\x00\x6e\x00\x67\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x18\ \x06\x07\x4c\x67\ \x00\x62\ \x00\x75\x00\x69\x00\x6c\x00\x64\x00\x6e\x00\x6f\x00\x74\x00\x69\x00\x66\x00\x79\x00\x2d\x00\x69\x00\x6e\x00\x61\x00\x63\x00\x74\ \x00\x69\x00\x76\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x20\ \x03\xb9\xe4\x87\ \x00\x62\ \x00\x75\x00\x69\x00\x6c\x00\x64\x00\x6e\x00\x6f\x00\x74\x00\x69\x00\x66\x00\x79\x00\x2d\x00\x66\x00\x61\x00\x69\x00\x6c\x00\x75\ \x00\x72\x00\x65\x00\x2d\x00\x62\x00\x75\x00\x69\x00\x6c\x00\x64\x00\x69\x00\x6e\x00\x67\x00\x2e\x00\x73\x00\x76\x00\x67\ \x00\x17\ \x02\xec\x1c\x87\ \x00\x62\ \x00\x75\x00\x69\x00\x6c\x00\x64\x00\x6e\x00\x6f\x00\x74\x00\x69\x00\x66\x00\x79\x00\x2d\x00\x66\x00\x61\x00\x69\x00\x6c\x00\x75\ \x00\x72\x00\x65\x00\x2e\x00\x73\x00\x76\x00\x67\ " qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ \x00\x00\x00\x12\x00\x02\x00\x00\x00\x05\x00\x00\x00\x03\ \x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ \x00\x00\x00\x56\x00\x00\x00\x00\x00\x01\x00\x00\x00\xd1\ \x00\x00\x01\x18\x00\x00\x00\x00\x00\x01\x00\x00\x03\x44\ \x00\x00\x00\xd2\x00\x00\x00\x00\x00\x01\x00\x00\x02\x74\ \x00\x00\x00\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x01\xa2\ " def qInitResources(): QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) def qCleanupResources(): QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) qInitResources() BuildNotify-0.3.5/buildnotifylib/generated/__init__.py0000644000175000017500000000002412315063423022732 0ustar anayanay00000000000000__author__ = 'anay' BuildNotify-0.3.5/buildnotifylib/generated/preferences_ui.py0000644000175000017500000002733012315063423024202 0ustar anayanay00000000000000# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'data/preferences.ui' # # Created: Tue Mar 18 20:49:08 2014 # by: PyQt4 UI code generator 4.9.3 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: _fromUtf8 = lambda s: s class Ui_Preferences(object): def setupUi(self, Preferences): Preferences.setObjectName(_fromUtf8("Preferences")) Preferences.resize(470, 394) Preferences.setSizeGripEnabled(False) self.tabWidget = QtGui.QTabWidget(Preferences) self.tabWidget.setGeometry(QtCore.QRect(10, 10, 451, 331)) self.tabWidget.setObjectName(_fromUtf8("tabWidget")) self.serversTab = QtGui.QWidget() self.serversTab.setObjectName(_fromUtf8("serversTab")) self.groupBox_2 = QtGui.QGroupBox(self.serversTab) self.groupBox_2.setGeometry(QtCore.QRect(10, 10, 431, 271)) self.groupBox_2.setObjectName(_fromUtf8("groupBox_2")) self.cctrayPathList = QtGui.QListView(self.groupBox_2) self.cctrayPathList.setGeometry(QtCore.QRect(0, 21, 419, 211)) self.cctrayPathList.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) self.cctrayPathList.setObjectName(_fromUtf8("cctrayPathList")) self.addButton = QtGui.QPushButton(self.groupBox_2) self.addButton.setGeometry(QtCore.QRect(380, 240, 41, 31)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.addButton.sizePolicy().hasHeightForWidth()) self.addButton.setSizePolicy(sizePolicy) self.addButton.setObjectName(_fromUtf8("addButton")) self.removeButton = QtGui.QPushButton(self.groupBox_2) self.removeButton.setGeometry(QtCore.QRect(330, 240, 41, 31)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.removeButton.sizePolicy().hasHeightForWidth()) self.removeButton.setSizePolicy(sizePolicy) self.removeButton.setObjectName(_fromUtf8("removeButton")) self.configureProjectButton = QtGui.QPushButton(self.groupBox_2) self.configureProjectButton.setEnabled(False) self.configureProjectButton.setGeometry(QtCore.QRect(200, 240, 116, 30)) self.configureProjectButton.setObjectName(_fromUtf8("configureProjectButton")) self.tabWidget.addTab(self.serversTab, _fromUtf8("")) self.notificationsTab = QtGui.QWidget() self.notificationsTab.setObjectName(_fromUtf8("notificationsTab")) self.groupBox = QtGui.QGroupBox(self.notificationsTab) self.groupBox.setGeometry(QtCore.QRect(10, 10, 421, 131)) self.groupBox.setFlat(False) self.groupBox.setCheckable(False) self.groupBox.setObjectName(_fromUtf8("groupBox")) self.layoutWidget_2 = QtGui.QWidget(self.groupBox) self.layoutWidget_2.setGeometry(QtCore.QRect(0, 30, 401, 101)) self.layoutWidget_2.setObjectName(_fromUtf8("layoutWidget_2")) self.gridLayout_2 = QtGui.QGridLayout(self.layoutWidget_2) self.gridLayout_2.setMargin(0) self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) self.successfulBuildsCheckbox = QtGui.QCheckBox(self.layoutWidget_2) self.successfulBuildsCheckbox.setObjectName(_fromUtf8("successfulBuildsCheckbox")) self.gridLayout_2.addWidget(self.successfulBuildsCheckbox, 0, 0, 1, 1) self.brokenBuildsCheckbox = QtGui.QCheckBox(self.layoutWidget_2) self.brokenBuildsCheckbox.setObjectName(_fromUtf8("brokenBuildsCheckbox")) self.gridLayout_2.addWidget(self.brokenBuildsCheckbox, 0, 1, 1, 1) self.fixedBuildsCheckbox = QtGui.QCheckBox(self.layoutWidget_2) self.fixedBuildsCheckbox.setObjectName(_fromUtf8("fixedBuildsCheckbox")) self.gridLayout_2.addWidget(self.fixedBuildsCheckbox, 1, 0, 1, 1) self.stillFailingBuildsCheckbox = QtGui.QCheckBox(self.layoutWidget_2) self.stillFailingBuildsCheckbox.setObjectName(_fromUtf8("stillFailingBuildsCheckbox")) self.gridLayout_2.addWidget(self.stillFailingBuildsCheckbox, 1, 1, 1, 1) self.connectivityIssuesCheckbox = QtGui.QCheckBox(self.layoutWidget_2) self.connectivityIssuesCheckbox.setMaximumSize(QtCore.QSize(200, 16777215)) self.connectivityIssuesCheckbox.setObjectName(_fromUtf8("connectivityIssuesCheckbox")) self.gridLayout_2.addWidget(self.connectivityIssuesCheckbox, 2, 0, 1, 1) self.groupBox_3 = QtGui.QGroupBox(self.notificationsTab) self.groupBox_3.setGeometry(QtCore.QRect(10, 160, 401, 111)) self.groupBox_3.setObjectName(_fromUtf8("groupBox_3")) self.scriptCheckbox = QtGui.QCheckBox(self.groupBox_3) self.scriptCheckbox.setGeometry(QtCore.QRect(0, 20, 401, 31)) self.scriptCheckbox.setObjectName(_fromUtf8("scriptCheckbox")) self.horizontalLayoutWidget_2 = QtGui.QWidget(self.groupBox_3) self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(10, 50, 391, 51)) self.horizontalLayoutWidget_2.setObjectName(_fromUtf8("horizontalLayoutWidget_2")) self.horizontalLayout_3 = QtGui.QHBoxLayout(self.horizontalLayoutWidget_2) self.horizontalLayout_3.setMargin(0) self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3")) self.scriptLabel = QtGui.QLabel(self.horizontalLayoutWidget_2) self.scriptLabel.setObjectName(_fromUtf8("scriptLabel")) self.horizontalLayout_3.addWidget(self.scriptLabel) self.scriptLineEdit = QtGui.QLineEdit(self.horizontalLayoutWidget_2) self.scriptLineEdit.setEnabled(False) self.scriptLineEdit.setText(_fromUtf8("")) self.scriptLineEdit.setObjectName(_fromUtf8("scriptLineEdit")) self.horizontalLayout_3.addWidget(self.scriptLineEdit) self.tabWidget.addTab(self.notificationsTab, _fromUtf8("")) self.miscTab = QtGui.QWidget() self.miscTab.setObjectName(_fromUtf8("miscTab")) self.showLastBuildTimeCheckbox = QtGui.QCheckBox(self.miscTab) self.showLastBuildTimeCheckbox.setGeometry(QtCore.QRect(10, 60, 351, 31)) self.showLastBuildTimeCheckbox.setObjectName(_fromUtf8("showLastBuildTimeCheckbox")) self.horizontalLayoutWidget = QtGui.QWidget(self.miscTab) self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 421, 41)) self.horizontalLayoutWidget.setObjectName(_fromUtf8("horizontalLayoutWidget")) self.horizontalLayout_2 = QtGui.QHBoxLayout(self.horizontalLayoutWidget) self.horizontalLayout_2.setMargin(0) self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) self.pollingIntervalLabel = QtGui.QLabel(self.horizontalLayoutWidget) self.pollingIntervalLabel.setObjectName(_fromUtf8("pollingIntervalLabel")) self.horizontalLayout_2.addWidget(self.pollingIntervalLabel) self.pollingIntervalSpinBox = QtGui.QSpinBox(self.horizontalLayoutWidget) self.pollingIntervalSpinBox.setMinimumSize(QtCore.QSize(130, 0)) self.pollingIntervalSpinBox.setMaximumSize(QtCore.QSize(130, 16777215)) self.pollingIntervalSpinBox.setWrapping(False) self.pollingIntervalSpinBox.setMinimum(1) self.pollingIntervalSpinBox.setMaximum(60) self.pollingIntervalSpinBox.setSingleStep(1) self.pollingIntervalSpinBox.setObjectName(_fromUtf8("pollingIntervalSpinBox")) self.horizontalLayout_2.addWidget(self.pollingIntervalSpinBox) self.tabWidget.addTab(self.miscTab, _fromUtf8("")) self.buttonBox = QtGui.QDialogButtonBox(Preferences) self.buttonBox.setGeometry(QtCore.QRect(40, 350, 421, 32)) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName(_fromUtf8("buttonBox")) self.scriptLabel.setBuddy(self.scriptLineEdit) self.pollingIntervalLabel.setBuddy(self.pollingIntervalSpinBox) self.retranslateUi(Preferences) self.tabWidget.setCurrentIndex(0) QtCore.QObject.connect(self.scriptCheckbox, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.scriptLineEdit.setEnabled) QtCore.QMetaObject.connectSlotsByName(Preferences) def retranslateUi(self, Preferences): Preferences.setWindowTitle(QtGui.QApplication.translate("Preferences", "Preferences", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox_2.setTitle(QtGui.QApplication.translate("Preferences", "Monitored servers", None, QtGui.QApplication.UnicodeUTF8)) self.addButton.setToolTip(QtGui.QApplication.translate("Preferences", "Add", None, QtGui.QApplication.UnicodeUTF8)) self.addButton.setText(QtGui.QApplication.translate("Preferences", "+", None, QtGui.QApplication.UnicodeUTF8)) self.removeButton.setToolTip(QtGui.QApplication.translate("Preferences", "Remove", None, QtGui.QApplication.UnicodeUTF8)) self.removeButton.setText(QtGui.QApplication.translate("Preferences", "-", None, QtGui.QApplication.UnicodeUTF8)) self.configureProjectButton.setText(QtGui.QApplication.translate("Preferences", "Configure", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.serversTab), QtGui.QApplication.translate("Preferences", "Servers", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox.setTitle(QtGui.QApplication.translate("Preferences", "Notification settings", None, QtGui.QApplication.UnicodeUTF8)) self.successfulBuildsCheckbox.setText(QtGui.QApplication.translate("Preferences", "successful builds", None, QtGui.QApplication.UnicodeUTF8)) self.brokenBuildsCheckbox.setText(QtGui.QApplication.translate("Preferences", "broken builds", None, QtGui.QApplication.UnicodeUTF8)) self.fixedBuildsCheckbox.setText(QtGui.QApplication.translate("Preferences", "fixed builds", None, QtGui.QApplication.UnicodeUTF8)) self.stillFailingBuildsCheckbox.setText(QtGui.QApplication.translate("Preferences", "still failing builds", None, QtGui.QApplication.UnicodeUTF8)) self.connectivityIssuesCheckbox.setText(QtGui.QApplication.translate("Preferences", "connectivity issues", None, QtGui.QApplication.UnicodeUTF8)) self.groupBox_3.setTitle(QtGui.QApplication.translate("Preferences", "Custom notifications", None, QtGui.QApplication.UnicodeUTF8)) self.scriptCheckbox.setText(QtGui.QApplication.translate("Preferences", "Execute script for notifications", None, QtGui.QApplication.UnicodeUTF8)) self.scriptLabel.setText(QtGui.QApplication.translate("Preferences", "Script", None, QtGui.QApplication.UnicodeUTF8)) self.scriptLineEdit.setToolTip(QtGui.QApplication.translate("Preferences", "#status# and #projects# would be replaced by the build status and projects respectively", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.notificationsTab), QtGui.QApplication.translate("Preferences", "Notifications", None, QtGui.QApplication.UnicodeUTF8)) self.showLastBuildTimeCheckbox.setText(QtGui.QApplication.translate("Preferences", "show last build time for each project", None, QtGui.QApplication.UnicodeUTF8)) self.pollingIntervalLabel.setText(QtGui.QApplication.translate("Preferences", "Server polling interval", None, QtGui.QApplication.UnicodeUTF8)) self.pollingIntervalSpinBox.setSuffix(QtGui.QApplication.translate("Preferences", " minutes", None, QtGui.QApplication.UnicodeUTF8)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.miscTab), QtGui.QApplication.translate("Preferences", "Misc", None, QtGui.QApplication.UnicodeUTF8)) import icons_rc BuildNotify-0.3.5/buildnotifylib/preferences.py0000644000175000017500000000763312315063423021553 0ustar anayanay00000000000000from PyQt4 import QtCore from PyQt4 import QtGui from buildnotifylib.generated.preferences_ui import Ui_Preferences from server_configuration_dialog import ServerConfigurationDialog class PreferencesDialog(QtGui.QDialog): addServerTemplateText = "http://[host]:[port]/dashboard/cctray.xml" def __init__(self, conf, parent=None): QtGui.QDialog.__init__(self, parent) self.conf = conf self.ui = Ui_Preferences() self.ui.setupUi(self) self.checkboxes = dict(successfulBuild=self.ui.successfulBuildsCheckbox, brokenBuild=self.ui.brokenBuildsCheckbox, fixedBuild=self.ui.fixedBuildsCheckbox, stillFailingBuild=self.ui.stillFailingBuildsCheckbox, connectivityIssues=self.ui.connectivityIssuesCheckbox, lastBuildTimeForProject=self.ui.showLastBuildTimeCheckbox) self.set_values_from_config() # Connect up the buttons. self.connect(self.ui.addButton, QtCore.SIGNAL("clicked()"), self.add_server) self.connect(self.ui.removeButton, QtCore.SIGNAL("clicked()"), self.remove_element) self.connect(self.ui.buttonBox, QtCore.SIGNAL("accepted()"), QtCore.SLOT("accept()")) self.connect(self.ui.configureProjectButton, QtCore.SIGNAL("clicked()"), self.configure_projects) def set_values_from_config(self): self.cctray_urls_model = QtGui.QStringListModel(self.conf.get_urls()) self.ui.cctrayPathList.setModel(self.cctray_urls_model) self.ui.cctrayPathList.clicked.connect(lambda x: self.item_selection_changed(True)) self.ui.removeButton.clicked.connect(lambda x: self.item_selection_changed(False)) for key, checkbox in self.checkboxes.iteritems(): checkbox.setChecked(self.conf.get_value(str(key))) self.ui.pollingIntervalSpinBox.setValue(self.conf.get_interval()) self.ui.scriptCheckbox.setChecked(self.conf.get_custom_script_enabled()) self.ui.scriptLineEdit.setText(self.conf.get_custom_script()) def item_selection_changed(self, status): self.ui.configureProjectButton.setEnabled(status) def add_server(self): server_configuration_dialog = ServerConfigurationDialog(True, self.addServerTemplateText, self.conf, self) if server_configuration_dialog.exec_() == QtGui.QDialog.Accepted: url = server_configuration_dialog.save() urls = self.ui.cctrayPathList.model().stringList() urls.append(url) self.cctray_urls_model = QtGui.QStringListModel(urls) self.ui.cctrayPathList.setModel(self.cctray_urls_model) def remove_element(self): index = self.ui.cctrayPathList.selectionModel().currentIndex() urls = self.ui.cctrayPathList.model().stringList() urls.removeAt(index.row()) self.cctray_urls_model = QtGui.QStringListModel(urls) self.ui.cctrayPathList.setModel(self.cctray_urls_model) def configure_projects(self): url = str(self.ui.cctrayPathList.selectionModel().currentIndex().data().toString()) if not url: return server_configuration_dialog = ServerConfigurationDialog(False, url, self.conf, self) if server_configuration_dialog.exec_() == QtGui.QDialog.Accepted: server_configuration_dialog.save() def get_urls(self): return self.ui.cctrayPathList.model().stringList() def get_interval(self): return self.ui.pollingIntervalSpinBox.value() def get_selections(self): return map(lambda (key, checkbox): (key, checkbox.isChecked()), self.checkboxes.items()) def save(self): self.conf.update_urls(self.get_urls()) self.conf.set_interval(self.get_interval()) self.conf.set_custom_script(self.ui.scriptLineEdit.text(), self.ui.scriptCheckbox.isChecked()) self.conf.set_custom_script_enabled(self.ui.scriptCheckbox.isChecked()) for key, value in self.get_selections(): self.conf.set_value(key, value) BuildNotify-0.3.5/buildnotifylib/config.py0000644000175000017500000000613712315063423020515 0ustar anayanay00000000000000from PyQt4 import QtCore class Config: default_options = dict(successfulBuild=False, brokenBuild=True, fixedBuild=True, stillFailingBuild=True, connectivityIssues=True, lastBuildTimeForProject=True) default_script = "echo #status# #projects# >> /tmp/buildnotify.log" CUSTOM_SCRIPT = "notifications/custom_script" SCRIPT_ENABLED = "notifications/custom_script_enabled" INTERVAL_IN_MINUTES = "connection/interval_in_minutes" CONNECTION_URLS = "connection/urls" EXCLUDES = "excludes/%s" TIMEZONE = "timezone/%s" VALUES = "values/%s" def __init__(self): self.settings = QtCore.QSettings("BuildNotify", "BuildNotify") self.timeout = self.get_with_default("connection/timeout", 10).toDouble()[0] self.interval = self.get_with_default(self.INTERVAL_IN_MINUTES, 2).toInt()[0] def get_with_default(self, key, default): if str(self.settings.value(key, "notset").toString()) == "notset": self.settings.setValue(key, default) return self.settings.value(key) def add_server_url(self, url): urls = self.get_urls() urls.append(url) self.update_urls(urls) def update_urls(self, urls): self.settings.setValue(self.CONNECTION_URLS, urls) def get_urls(self): return self.settings.value(self.CONNECTION_URLS, QtCore.QStringList()).toStringList() def set_interval(self, interval): self.settings.setValue(self.INTERVAL_IN_MINUTES, interval) def get_interval(self): return self.get_with_default(self.INTERVAL_IN_MINUTES, 2).toInt()[0] def get_interval_in_millis(self): return self.get_interval() * 1000 * 60 def get_value(self, key): return self.get_with_default(self.VALUES % key, self.default_options[key]).toBool() def set_value(self, key, value): return self.settings.setValue(self.VALUES % key, value) def get_timezone(self, url): return str(self.get_with_default(self.TIMEZONE % url, "US/Central").toString()) def get_project_timezone(self, url, server_url): # project level time zones can not be edited, so ditch the value # and just return the server's time zone return self.get_timezone(server_url) def set_project_timezone(self, url, timezone): self.settings.setValue(self.TIMEZONE % url, timezone) def set_project_excludes(self, url, excluded_project_names): self.settings.setValue(self.EXCLUDES % url, excluded_project_names) def get_project_excludes(self, url): return self.settings.value(self.EXCLUDES % url, QtCore.QStringList()).toStringList() def set_custom_script(self, user_script, status): script = user_script if status else self.default_script self.settings.setValue(self.CUSTOM_SCRIPT, script) def set_custom_script_enabled(self, status): self.settings.setValue(self.SCRIPT_ENABLED, status) def get_custom_script(self): return str(self.settings.value(self.CUSTOM_SCRIPT, self.default_script).toString()) def get_custom_script_enabled(self): return self.settings.value(self.SCRIPT_ENABLED, False).toBool() BuildNotify-0.3.5/buildnotifylib/core/0000755000175000017500000000000012315063430017615 5ustar anayanay00000000000000BuildNotify-0.3.5/buildnotifylib/core/projects.py0000644000175000017500000001061212315063423022022 0ustar anayanay00000000000000from xml.dom import minidom from http_connection import HttpConnection from dateutil.parser import parse from PyQt4.QtCore import QThread from PyQt4 import QtCore from timed_event import BackgroundEvent class Project: def __init__(self, props): self.name = props['name'] self.status = props['lastBuildStatus'] self.activity = props['activity'] self.last_build_time = parse(props['lastBuildTime']).replace(tzinfo=None) self.url = props['url'] self.server_url = props['server_url'] def get_build_status(self): return self.status + "." + self.activity class ContinuousIntegrationServer: def __init__(self, url, projects, unavailable=False): self.url = url self.projects = projects self.unavailable = unavailable def get_projects(self): return self.projects class FilteredContinuousIntegrationServer: def __init__(self, server, filter_projects): self.server = server self.filter_projects = filter_projects self.unavailable = server.unavailable self.url = server.url def get_projects(self): return filter(lambda project: project.name not in self.filter_projects, self.server.get_projects()) class OverallIntegrationStatus: def __init__(self, servers): self.servers = servers def get_build_status(self): build_status_mapping = self.to_map() seq = ['Failure.Building', 'Failure.Sleeping', 'Success.Building', 'Success.Sleeping', 'Failure.CheckingModifications', 'Success.CheckingModifications'] for status in seq: if len(build_status_mapping[status]) > 0: return status return None def get_failing_builds(self): return filter(lambda p: p.status == 'Failure', self.get_projects()) def to_map(self): status = dict( [('Success.Sleeping', []), ('Success.Building', []), ('Failure.CheckingModifications', []), ('Success.CheckingModifications', []), ('Failure.Sleeping', []), ('Failure.Building', []), ('Unknown.Building', []), ('Unknown.CheckingModifications', []), ('Unknown.Sleeping', []), ('Unknown.Unknown', [])]) for project in self.get_projects(): if project.get_build_status() in status: status[project.get_build_status()].append(project) else: status['Unknown.Unknown'].append(project) return status def get_projects(self): all_projects = [] for server in self.servers: if server.get_projects() is not None: all_projects.extend(server.get_projects()) return all_projects def unavailable_servers(self): return filter(lambda server: server.unavailable, self.servers) class ProjectsPopulator(QThread): def __init__(self, config, parent=None): QThread.__init__(self, parent) self.config = config self.listeners = [] def load_from_server(self): self.start() def reload(self): BackgroundEvent(self.process, self).run() def process(self): overall_status = [] for url in self.config.get_urls(): overall_status.append(self.check_nodes(str(url))) self.emit(QtCore.SIGNAL('updated_projects'), OverallIntegrationStatus(overall_status)) def run(self): self.process() def check_nodes(self, url): return FilteredContinuousIntegrationServer(ProjectLoader(url, self.config.timeout).get_data(), self.config.get_project_excludes(url)) class ProjectLoader: def __init__(self, url, timeout): self.url = url self.timeout = timeout def get_data(self): print "checking %s" % self.url try: data = HttpConnection().connect(self.url, self.timeout) except Exception, e: print e return ContinuousIntegrationServer(self.url, [], True) dom = minidom.parse(data) print "processed %s" % self.url projects = [] for node in dom.getElementsByTagName('Project'): projects.append(Project( {'name': node.getAttribute('name'), 'lastBuildStatus': node.getAttribute('lastBuildStatus'), 'activity': node.getAttribute('activity'), 'url': node.getAttribute('webUrl'), 'lastBuildTime': node.getAttribute('lastBuildTime'), 'server_url': self.url})) # WRONG return ContinuousIntegrationServer(self.url, projects) BuildNotify-0.3.5/buildnotifylib/core/http_connection.py0000644000175000017500000000167212315063423023375 0ustar anayanay00000000000000import socket import urllib2 import urlparse import base64 import platform class HttpConnection: def __init__(self): self.user_agent = "%s-%s" % ("BuildNotify", platform.platform()) def connect(self, url, timeout): socket.setdefaulttimeout(timeout) urlparts = urlparse.urlparse(url) username, password = urlparts.username, urlparts.password replace_string = "%s:%s@" % (username, password) host = urlparts.netloc.replace(replace_string, "") url_without_auth = urlparse.urlunparse((urlparts.scheme, host, urlparts.path, urlparts.params, urlparts.query, urlparts.fragment)) headers = {'User-Agent': self.user_agent} if username is not None: encodedstring = base64.encodestring("%s:%s" % (username, password))[:-1] headers["Authorization"] = "Basic %s" % encodedstring return urllib2.urlopen(urllib2.Request(url_without_auth, None, headers)) BuildNotify-0.3.5/buildnotifylib/core/distance_of_time.py0000644000175000017500000000253512315063423023472 0ustar anayanay00000000000000from datetime import datetime import pytz class DistanceOfTime: def __init__(self, from_date, timezone): self.from_date = from_date self.timezone = timezone def age(self): since_date = datetime.now(tz=pytz.timezone(self.timezone)).replace(tzinfo=None) distance_in_time = since_date - self.from_date distance_in_seconds = int(round(abs(distance_in_time.days * 86400 + distance_in_time.seconds))) distance_in_minutes = int(round(distance_in_seconds / 60)) if distance_in_minutes <= 1: return "1 minute" elif distance_in_minutes < 45: return "%s minutes" % distance_in_minutes elif distance_in_minutes < 90: return "1 hour" elif distance_in_minutes < 1440: return "%d hours" % (round(distance_in_minutes / 60.0)) elif distance_in_minutes < 2880: return "1 day" elif distance_in_minutes < 43220: return "%d days" % (round(distance_in_minutes / 1440)) elif distance_in_minutes < 86400: return "1 month" elif distance_in_minutes < 525600: return "%d months" % (round(distance_in_minutes / 43200)) elif distance_in_minutes < 1051200: return "1 year" else: return "over %d years" % (round(distance_in_minutes / 525600)) BuildNotify-0.3.5/buildnotifylib/core/__init__.py0000644000175000017500000000002412315063423021724 0ustar anayanay00000000000000__author__ = 'anay' BuildNotify-0.3.5/buildnotifylib/core/timed_event.py0000644000175000017500000000253012315063423022474 0ustar anayanay00000000000000from PyQt4 import QtCore from PyQt4.QtCore import QThread class TimedEvent: def __init__(self, parent, event_target, interval=2000): self.event_target = event_target self.parent = parent self.interval = interval def start(self): self.timer = QtCore.QTimer() self.parent.connect(self.timer, QtCore.SIGNAL('timeout()'), self.event_target) self.timer.setInterval(self.interval) self.timer.setSingleShot(True) self.timer.start() def set_interval(self, interval): self.interval = interval class RepeatTimedEvent: def __init__(self, parent, event_target, repeat_count): self.parent = parent self.repeat_count = repeat_count self.event_target = event_target self.event_happened_count = 0 def start(self): self.timed_event = TimedEvent(self.parent, self.on_event) self.timed_event.start() def on_event(self): self.event_target(self.event_happened_count) self.event_happened_count += 1 if self.event_happened_count != self.repeat_count: self.start() class BackgroundEvent(QThread): def __init__(self, task, parent=None): QThread.__init__(self, parent) self.task = task def run(self): data = self.task() self.emit(QtCore.SIGNAL('complete'), data) BuildNotify-0.3.5/buildnotifylib/build_icons.py0000644000175000017500000000303712315063423021536 0ustar anayanay00000000000000from PyQt4 import QtGui, QtCore class BuildIcons: success_sleeping = 'buildnotify-success' success_building = 'buildnotify-success-building' failure_sleeping = 'buildnotify-failure' failure_building = 'buildnotify-failure-building' unavailable = 'buildnotify-inactive' resource_path = ":/status/icons/%s.svg" theme_resource_path = "%s" def __init__(self): self.all_status = {'Success.Sleeping': self.success_sleeping, 'Success.CheckingModifications': self.success_sleeping, 'Success.Building': self.success_building, 'Failure.Sleeping': self.failure_sleeping, 'Failure.CheckingModifications': self.failure_sleeping, 'Failure.Building': self.failure_building, 'unavailable': self.unavailable} def for_status(self, status): return QtGui.QIcon.fromTheme(self.get_path(self.theme_resource_path, status), QtGui.QIcon(self.get_path(self.resource_path, status))) def for_aggregate_status(self, status, count): if count is "0": return self.for_status(status) icon = self.for_status(status) pixmap = icon.pixmap(22, 22) painter = QtGui.QPainter(pixmap) painter.setOpacity(1) painter.drawText(pixmap.rect(), QtCore.Qt.AlignCenter, count) painter.end() return QtGui.QIcon(pixmap) def get_path(self, resource_path, status): if status in self.all_status: return resource_path % self.all_status[status] return resource_path % self.all_status['unavailable'] BuildNotify-0.3.5/buildnotifylib/server_configuration_dialog.py0000644000175000017500000000530212315063423025015 0ustar anayanay00000000000000import pytz from PyQt4 import QtCore from PyQt4.QtCore import Qt from PyQt4 import QtGui from buildnotifylib.generated.server_configuration_ui import Ui_serverConfigurationDialog from buildnotifylib.core.timed_event import BackgroundEvent from buildnotifylib.core.projects import ProjectLoader class ServerConfigurationDialog(QtGui.QDialog): def __init__(self, editable, url, conf, parent=None): QtGui.QDialog.__init__(self, parent) self.ui = Ui_serverConfigurationDialog() self.ui.setupUi(self) self.editable = editable self.ui.addServerUrl.setText(url) self.conf = conf timezones = QtCore.QStringList(pytz.all_timezones) self.ui.timezoneList.addItems(timezones) self.ui.timezoneList.setCurrentIndex(timezones.indexOf(self.conf.get_timezone(self.server_url()))) if not editable: self.auto_load() self.connect(self.ui.loadUrlButton, QtCore.SIGNAL("clicked()"), self.fetch_data) def auto_load(self): self.ui.addServerUrl.setReadOnly(True) self.ui.loadUrlButton.setEnabled(False) self.fetch_data() def fetch_data(self): self.ui.loadUrlButton.setEnabled(False) self.event = BackgroundEvent(self.load_projects, self) self.connect(self.event, QtCore.SIGNAL('complete'), lambda data: self.load_data(data)) self.event.start() def load_projects(self): self.project_loader = ProjectLoader(self.server_url(), self.conf.timeout) return self.project_loader.get_data() def load_data(self, server): self.ui.loadUrlButton.setEnabled(self.editable) excluded_projects = self.conf.get_project_excludes(self.server_url()) projects_model = QtGui.QStandardItemModel() for project in server.projects: item = QtGui.QStandardItem(project.name) item.setCheckable(True) check = Qt.Unchecked if project.name in excluded_projects else Qt.Checked item.setCheckState(check) projects_model.appendRow(item) self.ui.projectsList.setModel(projects_model) def server_url(self): return str(self.ui.addServerUrl.text()) def save(self): projects_model = self.ui.projectsList.model() if projects_model is None: return self.server_url() excluded_projects = [str(projects_model.index(index, 0).data().toString()) for index in range(projects_model.rowCount()) if projects_model.index(index, 0).data(Qt.CheckStateRole) == Qt.Unchecked] self.conf.set_project_excludes(self.server_url(), excluded_projects) self.conf.set_project_timezone(self.server_url(), self.ui.timezoneList.currentText()) return self.server_url() BuildNotify-0.3.5/buildnotifylib/buildnotify.py0000644000175000017500000000440012315063423021567 0ustar anayanay00000000000000import sys from app_ui import AppUi from app_notification import AppNotification from config import Config from buildnotifylib.core.projects import ProjectsPopulator from PyQt4 import QtGui, QtCore from build_icons import BuildIcons from buildnotifylib.core.timed_event import TimedEvent, RepeatTimedEvent class BuildNotify: def __init__(self): self.conf = Config() self.app = QtGui.QApplication(sys.argv) self.build_icons = BuildIcons() self.app.setWindowIcon(self.build_icons.for_status("Success.Sleeping")) self.app.setQuitOnLastWindowClosed(False) self.ready = False self.timed_event = RepeatTimedEvent(self.app, self.delayed_start, 5) self.timed_event.start() sys.exit(self.app.exec_()) def delayed_start(self, event_count): if not QtGui.QSystemTrayIcon.isSystemTrayAvailable(): if event_count == 5: QtGui.QMessageBox.critical(None, "BuildNotify", "I couldn't detect any system tray on this system.") sys.exit(1) self.timed_event.start() if not self.ready: self.ready = True self.run_app() def run_app(self): self.projects_populator = ProjectsPopulator(self.conf, self.app) self.app.connect(self.projects_populator, QtCore.SIGNAL('updated_projects'), self.update_projects) self.app.connect(self.app, QtCore.SIGNAL('reload_project_data'), self.reload_project_data) self.app_ui = AppUi(self.app, self.conf, self.build_icons) self.app_notification = AppNotification(self.conf, self.app_ui.tray) self.auto_poll() def reload_project_data(self): self.projects_populator.reload() def update_projects(self, integration_status): self.app_notification.update_projects(integration_status) self.app_ui.update_projects(integration_status) def auto_poll(self): self.timed_event = TimedEvent(self.app, self.check_nodes) self.timed_event.set_interval(1000) self.timed_event.start() def check_nodes(self): self.projects_populator.load_from_server() self.timed_event.set_interval(self.conf.get_interval_in_millis()) self.timed_event.start() if __name__ == '__main__': BuildNotify() BuildNotify-0.3.5/buildnotifylib/project_status_notification.py0000644000175000017500000001074312315063423025065 0ustar anayanay00000000000000from datetime import datetime import subprocess class ProjectStatusNotification: def __init__(self, config, old_integration_status, current_integration_status, notification): self.config = config self.old_integration_status = old_integration_status self.current_integration_status = current_integration_status self.notification = notification self.timed_project_filter = TimedProjectFilter() def show_notifications(self): project_status = ProjectStatus(self.old_integration_status.get_projects(), self.current_integration_status.get_projects()) self.show_notification_msg(self.config.get_value("fixedBuild"), project_status.successful_builds(), "Fixed builds") self.show_notification_msg(self.config.get_value("brokenBuild"), project_status.failing_builds(), "Broken builds") self.show_notification_msg(self.config.get_value("stillFailingBuild"), project_status.still_failing_builds(), "Build is still failing") if self.current_integration_status.unavailable_servers() is not []: self.show_notification_msg(self.config.get_value("connectivityIssues"), self.timed_project_filter.filter(map(lambda server: server.url, self.current_integration_status.unavailable_servers())), "Connectivity issues") self.show_notification_msg(self.config.get_value("successfulBuild"), project_status.still_successful_builds(), "Yet another successful build") def show_notification_msg(self, show_notification, builds, message): if show_notification is False or builds == []: return self.notification.show_message(message, "\n".join(builds)) if self.config.get_custom_script_enabled(): command = self.config.get_custom_script().replace('#status#', message).replace('#projects#', ",".join(builds)) subprocess.Popen(command, shell=True) class TimedProjectFilter: map = dict() fact = [1, 2, 3, 5, 8, 13, 21] def __init__(self): pass def filter(self, urls): return filter(lambda url: self.is_new(url), urls) def is_new(self, url): if url not in self.map: self.map[url] = (datetime.now(), 1) return True connection_time, fail_count = self.map[url] fail_count += 1 if self.fact[len(self.fact) - 1] <= fail_count: fail_count = 1 self.map[url] = (connection_time, fail_count) return fail_count in self.fact class ProjectStatus: def __init__(self, old_projects, current_projects): self.old_projects = old_projects self.current_projects = current_projects def failing_builds(self): return self.filter_all(lambda project_tuple: project_tuple.has_failed()) def successful_builds(self): return self.filter_all(lambda project_tuple: project_tuple.has_succeeded()) def still_failing_builds(self): return self.filter_all(lambda project_tuple: project_tuple.has_been_failing()) def still_successful_builds(self): return self.filter_all(lambda project_tuple: project_tuple.has_been_successful()) def filter_all(self, filter_fn): project_tuples = map(lambda current_project: self.tuple_for(current_project), self.current_projects) project_tuples = filter(filter_fn, project_tuples) return map(lambda project_tuple: project_tuple.current_project.name, project_tuples) def tuple_for(self, new_project): for project in self.old_projects: if project.name == new_project.name: return ProjectTuple(new_project, project) return ProjectTuple(new_project, None) class ProjectTuple: def __init__(self, current_project, old_project): self.current_project = current_project self.old_project = old_project def has_failed(self): return self.status('Failure', 'Success') def has_succeeded(self): return self.status('Success', 'Failure') def has_been_successful(self): return (self.old_project is None) or (self.status('Success', 'Success') and self.different_builds()) def has_been_failing(self): return self.status('Failure', 'Failure') and self.different_builds() def status(self, new_status, old_status): return self.current_project.status == new_status and self.old_project is not None and self.old_project.status == old_status def different_builds(self): return self.current_project.last_build_time != self.old_project.last_build_time BuildNotify-0.3.5/buildnotifylib/__init__.py0000644000175000017500000000004412315063423020776 0ustar anayanay00000000000000from buildnotify import BuildNotify BuildNotify-0.3.5/buildnotifylib/notifications.py0000644000175000017500000000125112315063423022111 0ustar anayanay00000000000000try: import pynotify except ImportError: pass from PyQt4 import QtGui class Notification: def __init__(self, widget): self.widget = widget self.notification = None try: if pynotify.init(" buildnotify "): self.notification = pynotify.Notification("buildnotify", "buildnotify", None) except NameError: pass def show_message(self, title, text): if self.notification: self.notification.update(title, text, None) if self.notification is None or not self.notification.show(): self.widget.showMessage(title, text, QtGui.QSystemTrayIcon.Information, 3000) BuildNotify-0.3.5/buildnotifylib/app_ui.py0000644000175000017500000000146412315063423020523 0ustar anayanay00000000000000from PyQt4 import QtGui from time import strftime from app_menu import AppMenu class AppUi: def __init__(self, app, conf, build_icons): self.widget = QtGui.QWidget() self.app = app self.build_icons = build_icons self.tray = QtGui.QSystemTrayIcon(self.build_icons.for_status(None), self.widget) self.tray.show() self.app_menu = AppMenu(self.app, self.tray, self.widget, conf, self.build_icons); def update_projects(self, integration_status): count = str(len(integration_status.get_failing_builds())) self.tray.setIcon(self.build_icons.for_aggregate_status(integration_status.get_build_status(), count)) self.app_menu.update(integration_status.get_projects()) self.tray.setToolTip("Last checked: " + strftime("%Y-%m-%d %H:%M:%S")) BuildNotify-0.3.5/buildnotifylib/app_notification.py0000644000175000017500000000111412315063423022564 0ustar anayanay00000000000000from project_status_notification import ProjectStatusNotification from notifications import Notification class AppNotification: def __init__(self, config, widget): self.config = config self.notification = Notification(widget) self.integration_status = None def update_projects(self, new_integration_status): if self.integration_status is not None: ProjectStatusNotification(self.config, self.integration_status, new_integration_status, self.notification).show_notifications() self.integration_status = new_integration_status BuildNotify-0.3.5/buildnotifylib/version.py0000644000175000017500000000002312315063423020721 0ustar anayanay00000000000000VERSION = "0.3.5" BuildNotify-0.3.5/buildnotifylib/app_menu.py0000644000175000017500000000516212315063423021051 0ustar anayanay00000000000000import sys import webbrowser from PyQt4 import QtGui from PyQt4 import QtCore from buildnotifylib.core.distance_of_time import DistanceOfTime from preferences import PreferencesDialog from version import VERSION class AppMenu: def __init__(self, app, tray, widget, conf, build_icons): self.menu = QtGui.QMenu(widget) tray.setContextMenu(self.menu) self.conf = conf self.app = app self.build_icons = build_icons self.create_default_menu_items() def update(self, projects): projects.sort(lambda x, y: (x.last_build_time - y.last_build_time).days) self.menu.clear() for project in projects: self.create_menu_item(project.name, self.build_icons.for_status(project.get_build_status()), project.url, project.last_build_time, project.server_url) self.create_default_menu_items() def create_default_menu_items(self): self.menu.addSeparator() self.menu.addAction(QtGui.QAction("About", self.menu, triggered=self.about_clicked)) self.menu.addAction(QtGui.QAction("Preferences", self.menu, triggered=self.preferences_clicked)) self.menu.addAction(QtGui.QAction("Exit", self.menu, triggered=self.exit)) def about_clicked(self, widget): QtGui.QMessageBox.about(self.menu, "About BuildNotify %s" % VERSION, "BuildNotify %s has been developed using PyQt4 and serves as a build notification tool for cruise control. In case of any suggestions/bugs," % VERSION + "please visit http://bitbucket.org/Anay/buildnotify and provide your feedback.") def preferences_clicked(self, widget): self.preferences_dialog = PreferencesDialog(self.conf, self.menu) if self.preferences_dialog.exec_() == QtGui.QDialog.Accepted: self.preferences_dialog.save() self.app.emit(QtCore.SIGNAL('reload_project_data')) def exit(self, widget): sys.exit() def create_menu_item(self, label, icon, url, last_build_time, server_url): menu_item_label = label if self.conf.get_value("lastBuildTimeForProject"): menu_item_label = label + ", " + DistanceOfTime(last_build_time, self.conf.get_project_timezone(url, server_url)).age() + " ago" action = self.menu.addAction(icon, menu_item_label) action.setIconVisibleInMenu(True) receiver = lambda url=url: self.open_url(self, url) QtCore.QObject.connect(action, QtCore.SIGNAL('triggered()'), receiver) def open_url(self, something, url): webbrowser.open(url) BuildNotify-0.3.5/README0000755000175000017500000000054312315063423014535 0ustar anayanay00000000000000INSTALL: a) Execute buildnotifyapplet.py from the current directory to start buildnotify. b) Alternatively you can install using easy_install. b.1) Run easy_install BuildNotify b.2) Run /usr/bin/buildnotifyapplet.py or c:/python2.6/scripts/buildnotifyapplet.py to launch it. DEPENDENCIES Install pyqt4, pytz and python-dateutil. BuildNotify-0.3.5/MANIFEST.in0000644000175000017500000000004612315063423015406 0ustar anayanay00000000000000recursive-include buildnotifylib *.py