keithsalisbury-subtrac 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +7 -0
- data/Rakefile +56 -0
- data/VERSION.yml +4 -0
- data/bin/subtrac +40 -0
- data/lib/subtrac.rb +245 -0
- data/lib/subtrac/common/clients/index.wsgi +89 -0
- data/lib/subtrac/common/favicon.ico +0 -0
- data/lib/subtrac/common/images/trac/banner_bg.jpg +0 -0
- data/lib/subtrac/common/images/trac/bar_bg.gif +0 -0
- data/lib/subtrac/common/images/trac/footer_back.png +0 -0
- data/lib/subtrac/common/images/trac/main_bg.gif +0 -0
- data/lib/subtrac/common/images/trac/saint_logo_small.png +0 -0
- data/lib/subtrac/common/static/404.html +14 -0
- data/lib/subtrac/common/styles/trac.css +222 -0
- data/lib/subtrac/common/trac.ini +178 -0
- data/lib/subtrac/config/config.yml +54 -0
- data/lib/subtrac/passwords +1 -0
- data/lib/subtrac/shared/trac.ini +178 -0
- data/lib/subtrac/templates/location.erb +16 -0
- data/lib/subtrac/templates/projects/blank/svn/branches/README +0 -0
- data/lib/subtrac/templates/projects/blank/svn/tags/README +0 -0
- data/lib/subtrac/templates/projects/blank/svn/trunk/README +0 -0
- data/lib/subtrac/templates/projects/blank/trac/wiki/WikiStart +57 -0
- data/lib/subtrac/templates/projects/new/svn/trunk/trac/wiki/WikiStart +46 -0
- data/lib/subtrac/templates/projects/new/trac/wiki/WikiStart +23 -0
- data/lib/subtrac/templates/projects/trac_theme/svn/trunk/index/index.html +22 -0
- data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/layout.html +56 -0
- data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/site.html +27 -0
- data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/theme.html +86 -0
- data/lib/subtrac/templates/projects/trac_theme/trac/wiki/WikiStart +4 -0
- data/lib/subtrac/templates/trac.erb +25 -0
- data/lib/subtrac/templates/vhost.erb +35 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/controller.py +419 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.cfg +3 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.py +20 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action.py +28 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action_email.py +168 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action_zendesk_forum.py +137 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/admin.py +91 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/api.py +199 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/client.py +105 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/events.py +287 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/eventsadmin.py +71 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/htdocs/clients.css +4 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/model.py +135 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/processor.py +70 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/reportmanager.py +142 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/reports.py +231 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary.py +27 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_milestone.py +152 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_ticketchanges.py +160 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_client_events.html +124 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_clients.html +134 -0
- data/lib/subtrac/trac-plugins/clientsplugin/cron/changes.xslt +132 -0
- data/lib/subtrac/trac-plugins/clientsplugin/cron/run-client-event +97 -0
- data/lib/subtrac/trac-plugins/clientsplugin/cron/summary.xslt +161 -0
- data/lib/subtrac/trac-plugins/clientsplugin/setup.py +43 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/__init__.py +4 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/burndownchart.py +273 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursinplaceeditor.py +44 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursremaining.py +36 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery-1.2.3.min.js +32 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.js +409 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.mini.js +30 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/templates/edithours.html +53 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/burndownchart.py +181 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/hoursremaining.py +66 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/workloadchart.py +47 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/utils.py +93 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/workloadchart.py +86 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/setup.py +20 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/SumRollups.js +23 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/adw_tracdb.py +128 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/git-post-receive +40 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac-post-commit.py +285 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac_billing.py +173 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/mail.py +164 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/setup.py +69 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/__init__.py +1 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/api.py +292 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/blackmagic.py +172 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/dbhelper.py +178 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/billingplugin.css +25 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/field_disabler.js +6 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/formatDate.js +356 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_centerwindow.js +100 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_followscroll.js +84 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/wz_tooltip.js +1149 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/linkifyer.js +119 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/query.js +73 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/ticket.js +165 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/query_webui.py +28 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reportmanager.py +221 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports.py +675 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports_filter.py +150 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/statuses.py +25 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/tande_filters.py +131 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.cs +84 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.html +104 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_daemon.py +194 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_policy.py +62 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_webui.py +28 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/usermanual.py +127 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/webui.py +129 -0
- data/lib/subtrac/trac-plugins/worklogplugin/setup.py +29 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/__init__.py +1 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/api.py +187 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.css +40 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.js +67 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.mousewheel.pack.js +12 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.timeentry.pack.js +7 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/tracWorklog.js +40 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.css +208 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.js +1439 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.png +0 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.xcf +0 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/worklogplugin.css +80 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstart.png +0 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstop.png +0 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/manager.py +336 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/reports.py +598 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog.html +45 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_stop.html +70 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_user.html +40 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_webadminui.html +59 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_daemon.py +33 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_filter.py +153 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/timeline_hook.py +96 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/usermanual.py +29 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/util.py +31 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/webadminui.py +47 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/webui.py +174 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/xmlrpc.py +73 -0
- data/lib/subtrac/version.rb +4 -0
- metadata +191 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
from datetime import timedelta
|
2
|
+
from estimationtools.utils import parse_options, execute_query, get_estimation_field, get_estimation_suffix, get_closed_states
|
3
|
+
from trac.util.html import Markup
|
4
|
+
from trac.wiki.macros import WikiMacroBase
|
5
|
+
import copy
|
6
|
+
|
7
|
+
DEFAULT_OPTIONS = {'width': '400', 'height': '100', 'color': 'ff9900'}
|
8
|
+
|
9
|
+
class WorkloadChart(WikiMacroBase):
|
10
|
+
"""Creates workload chart for the selected tickets.
|
11
|
+
|
12
|
+
This macro creates a pie chart that shows the remaining estimated workload per ticket owner,
|
13
|
+
and the remaining work days.
|
14
|
+
It has the following parameters:
|
15
|
+
* a comma-separated list of query parameters for the ticket selection, in the form "key=value" as specified in TracQuery#QueryLanguage.
|
16
|
+
* `width`: width of resulting diagram (defaults to 400)
|
17
|
+
* `height`: height of resulting diagram (defaults to 100)
|
18
|
+
* `color`: color specified as 6-letter string of hexadecimal values in the format `RRGGBB`.
|
19
|
+
Defaults to `ff9900`, a nice orange.
|
20
|
+
|
21
|
+
Examples:
|
22
|
+
{{{
|
23
|
+
[[WorkloadChart(milestone=Sprint 1)]]
|
24
|
+
[[WorkloadChart(milestone=Sprint 1, width=600, height=100, color=00ff00)]]
|
25
|
+
}}}
|
26
|
+
"""
|
27
|
+
|
28
|
+
estimation_field = get_estimation_field()
|
29
|
+
estimation_suffix = get_estimation_suffix()
|
30
|
+
closed_states = get_closed_states()
|
31
|
+
|
32
|
+
def render_macro(self, req, name, content):
|
33
|
+
db = self.env.get_db_cnx()
|
34
|
+
# prepare options
|
35
|
+
options, query_args = parse_options(db, content, copy.copy(DEFAULT_OPTIONS))
|
36
|
+
|
37
|
+
query_args[self.estimation_field + "!"] = None
|
38
|
+
tickets = execute_query(self.env, req, query_args)
|
39
|
+
|
40
|
+
sum = 0.0
|
41
|
+
estimations = {}
|
42
|
+
for ticket in tickets:
|
43
|
+
if ticket['status'] in self.closed_states:
|
44
|
+
continue
|
45
|
+
try:
|
46
|
+
estimation = float(ticket[self.estimation_field])
|
47
|
+
owner = ticket['owner']
|
48
|
+
sum += estimation
|
49
|
+
if estimations.has_key(owner):
|
50
|
+
estimations[owner] += estimation
|
51
|
+
else:
|
52
|
+
estimations[owner] = estimation
|
53
|
+
except:
|
54
|
+
pass
|
55
|
+
|
56
|
+
estimations_string = []
|
57
|
+
labels = []
|
58
|
+
for owner, estimation in estimations.iteritems():
|
59
|
+
labels.append("%s %s%s" % (owner, str(int(estimation)), self.estimation_suffix))
|
60
|
+
estimations_string.append(str(int(estimation)))
|
61
|
+
|
62
|
+
# Title
|
63
|
+
title = 'Workload'
|
64
|
+
|
65
|
+
# calculate remaining work time
|
66
|
+
if options.get('today') and options.get('enddate'):
|
67
|
+
currentdate = options['today']
|
68
|
+
day = timedelta(days=1)
|
69
|
+
days_remaining = 0
|
70
|
+
while currentdate <= options['enddate']:
|
71
|
+
if currentdate.weekday() < 5:
|
72
|
+
days_remaining += 1
|
73
|
+
currentdate += day
|
74
|
+
title += ' %s%s (%s workdays left)' % (int(sum), self.estimation_suffix, days_remaining)
|
75
|
+
|
76
|
+
return Markup("<img src=\"http://chart.apis.google.com/chart?"
|
77
|
+
"chs=%sx%s"
|
78
|
+
"&chf=bg,s,00000000"
|
79
|
+
"&chd=t:%s"
|
80
|
+
"&cht=p3"
|
81
|
+
"&chtt=%s"
|
82
|
+
"&chl=%s"
|
83
|
+
"&chco=%s\" "
|
84
|
+
"alt=\'Workload Chart\' />"
|
85
|
+
% (options['width'], options['height'], ",".join(estimations_string),
|
86
|
+
title, "|".join(labels), options['color']))
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: iso-8859-1 -*-
|
3
|
+
|
4
|
+
from setuptools import setup
|
5
|
+
|
6
|
+
setup(
|
7
|
+
name = 'EstimationTools',
|
8
|
+
author = 'Joachim Hoessler',
|
9
|
+
author_email = 'hoessler@gmail.com',
|
10
|
+
description = 'Trac plugin for visualizing and quick editing of effort estimations',
|
11
|
+
version = '0.4',
|
12
|
+
license='BSD',
|
13
|
+
packages=['estimationtools'],
|
14
|
+
package_data = { 'estimationtools': ['htdocs/*.js', 'templates/*.html'] },
|
15
|
+
entry_points = {
|
16
|
+
'trac.plugins': [
|
17
|
+
'estimationtools = estimationtools'
|
18
|
+
]
|
19
|
+
}
|
20
|
+
)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
//javascript:(function(){var filter = function (fn, list, thisObj) { var arr = []; for (var i = 0; i < list.length; i++) { if (fn.call(thisObj, list[i])) { arr.push(list[i]);return arr;var sum = 0; var tds = filter(function(td){return td.innerHTML =='None';document.getElementsByTagName('td'),this); for(var i = 0; i < tds.length; i++){sum += parseFloat(tds[i].parentNode.childNodes[5].innerHTML);alert('Total hours: ' + sum);})();
|
2
|
+
javascript:(
|
3
|
+
function(){
|
4
|
+
var filter = function (fn, list, thisObj) {
|
5
|
+
var arr = [];
|
6
|
+
for (var i = 0; i < list.length; i++) {
|
7
|
+
if (fn.call(thisObj, list[i])) {
|
8
|
+
arr.push(list[i]);
|
9
|
+
}
|
10
|
+
}
|
11
|
+
return arr;
|
12
|
+
};
|
13
|
+
var sum = 0;
|
14
|
+
var tds = filter(function(td){
|
15
|
+
return td.innerHTML =='None';
|
16
|
+
},
|
17
|
+
document.getElementsByTagName('td'),this);
|
18
|
+
for(var i = 0; i < tds.length; i++){
|
19
|
+
sum += parseFloat(tds[i].parentNode.childNodes[5].innerHTML);
|
20
|
+
}
|
21
|
+
alert('Total hours: ' + sum);
|
22
|
+
}
|
23
|
+
)();
|
@@ -0,0 +1,128 @@
|
|
1
|
+
from pysqlite2 import dbapi2 as sqlite
|
2
|
+
from datetime import datetime, timedelta
|
3
|
+
from time import mktime
|
4
|
+
import os
|
5
|
+
|
6
|
+
tracDir = '/var/trac'
|
7
|
+
projectDBLocationFormat = '/var/trac/%s/db/trac.db'
|
8
|
+
# all directories in the trac directory
|
9
|
+
projects = [f for f in os.listdir(tracDir) if os.path.isdir('/'.join ([tracDir, f]))]
|
10
|
+
|
11
|
+
def makeDb(proj):
|
12
|
+
return projectDBLocationFormat % proj
|
13
|
+
|
14
|
+
def collectResultsFromAllTracs( sql ):
|
15
|
+
lst = []
|
16
|
+
for proj in projects:
|
17
|
+
try:
|
18
|
+
lst.extend([( proj , getResultSet( proj, sql ))])
|
19
|
+
except Exception, e:
|
20
|
+
print "collectResultsFromAllTracs: sql failed to execute on %s : %s " % (proj, e.args)
|
21
|
+
|
22
|
+
return lst
|
23
|
+
|
24
|
+
def executeAgainstAllTracs( sql ):
|
25
|
+
for proj in projects:
|
26
|
+
try:
|
27
|
+
executeNonQuery( proj, sql )
|
28
|
+
except:
|
29
|
+
print "executeAgainstAllTracs: sql failed to execute on %s" % proj
|
30
|
+
|
31
|
+
|
32
|
+
def executeNonQuery(proj, sql, *params):
|
33
|
+
"""Executes the query on the given project"""
|
34
|
+
con = sqlite.connect(makeDb(proj))
|
35
|
+
cur = con.cursor()
|
36
|
+
try:
|
37
|
+
cur.execute(sql, params)
|
38
|
+
con.commit()
|
39
|
+
finally:
|
40
|
+
cur.close()
|
41
|
+
con.close()
|
42
|
+
|
43
|
+
def getColumnAsList(db, sql, col=0, *params):
|
44
|
+
return [valueList[col] for valueList in get_all(db, sql, *params)[1]]
|
45
|
+
|
46
|
+
def getScalar(proj , sql, col=0, *params):
|
47
|
+
db = sqlite.connect(makeDb(proj))
|
48
|
+
cur = db.cursor()
|
49
|
+
try:
|
50
|
+
cur.execute(sql, params)
|
51
|
+
data = cur.fetchone()
|
52
|
+
finally:
|
53
|
+
cur.close()
|
54
|
+
return data[col]
|
55
|
+
|
56
|
+
|
57
|
+
def getVector(proj, sql, *params):
|
58
|
+
db = sqlite.connect(makeDb(proj))
|
59
|
+
cur = db.cursor()
|
60
|
+
try:
|
61
|
+
cur.execute(sql, params)
|
62
|
+
data = cur.fetchone()
|
63
|
+
finally:
|
64
|
+
cur.close()
|
65
|
+
return data
|
66
|
+
|
67
|
+
def getAll(proj, sql, *params):
|
68
|
+
"""Executes the query and returns the (description, data)"""
|
69
|
+
con = sqlite.connect(makeDb(proj))
|
70
|
+
cur = con.cursor()
|
71
|
+
try:
|
72
|
+
cur.execute(sql, params)
|
73
|
+
data = cur.fetchall()
|
74
|
+
desc = cur.description
|
75
|
+
finally:
|
76
|
+
cur.close()
|
77
|
+
con.close()
|
78
|
+
return (desc, data)
|
79
|
+
|
80
|
+
def getResultSet(proj, sql, *params):
|
81
|
+
"""Executes the query and returns a Result Set"""
|
82
|
+
return ResultSet(getAll(proj, sql, *params))
|
83
|
+
|
84
|
+
def _columnName( columnDescription ):
|
85
|
+
""" given a the columnHeader from the result set from getAll gives you the column Headers """
|
86
|
+
return columnDescription[0];
|
87
|
+
|
88
|
+
class ResultSet:
|
89
|
+
""" the result of calling getResultSet """
|
90
|
+
def __init__ (self, (columnDescription, rows)):
|
91
|
+
self.columnDescription, self.rows = columnDescription, rows
|
92
|
+
self.columnNames = [_columnName(_) for _ in self.columnDescription]
|
93
|
+
self.columnMap = self.getColumnMap()
|
94
|
+
|
95
|
+
def getColumnMap ( self ):
|
96
|
+
"""This function will take the result set from getAll and will
|
97
|
+
return a hash of the column names to their index """
|
98
|
+
h = {}
|
99
|
+
i = 0
|
100
|
+
if self.columnDescription:
|
101
|
+
for col in self.columnNames:
|
102
|
+
h[ col ] = i
|
103
|
+
i+=1
|
104
|
+
return h;
|
105
|
+
|
106
|
+
def value(self, col, row ):
|
107
|
+
""" given a row(list or idx) and a column( name or idx ), retrieve the appropriate value"""
|
108
|
+
tcol = type(col)
|
109
|
+
trow = type(row)
|
110
|
+
if tcol == str:
|
111
|
+
if(trow == list or trow == tuple):
|
112
|
+
return row[self.columnMap[col]]
|
113
|
+
elif(trow == int):
|
114
|
+
return self.rows[row][self.columnMap[col]]
|
115
|
+
else:
|
116
|
+
print ("rs.value Type Failed col:%s row:%s" % (type(col), type(row)))
|
117
|
+
elif tcol == int:
|
118
|
+
if(trow == list or trow == tuple):
|
119
|
+
return row[col]
|
120
|
+
elif(trow == int):
|
121
|
+
return self.rows[row][col]
|
122
|
+
else:
|
123
|
+
print ("rs.value Type Failed col:%s row:%s" % (type(col), type(row)))
|
124
|
+
else:
|
125
|
+
print ("rs.value Type Failed col:%s row:%s" % (type(col), type(row)))
|
126
|
+
|
127
|
+
|
128
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# An example hook script for the post-receive event
|
4
|
+
#
|
5
|
+
# This script is run after receive-pack has accepted a pack and the
|
6
|
+
# repository has been updated. It is passed arguments in through stdin
|
7
|
+
# in the form
|
8
|
+
# <oldrev> <newrev> <refname>
|
9
|
+
# For example:
|
10
|
+
# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
|
11
|
+
#
|
12
|
+
# see contrib/hooks/ for an sample, or uncomment the next line (on debian)
|
13
|
+
#
|
14
|
+
|
15
|
+
|
16
|
+
TRAC_ENV="YOUR TRAC ENV HERE"
|
17
|
+
LOG="/dev/null"
|
18
|
+
echo "in git post commit: $TRAC_ENV" | cat >>$LOG
|
19
|
+
|
20
|
+
while read oval nval ref ; do
|
21
|
+
if expr "$oval" : '0*$' >/dev/null
|
22
|
+
then
|
23
|
+
git-rev-list "$nval"
|
24
|
+
else
|
25
|
+
git-rev-list "$nval" "^$oval"
|
26
|
+
fi | while read com ; do
|
27
|
+
echo "posting a comment to trac" | cat >>$LOG
|
28
|
+
/usr/bin/python /usr/share/trac/contrib/trac-post-commit.py \
|
29
|
+
-p "$TRAC_ENV" \
|
30
|
+
-r "$com" \
|
31
|
+
-u "$(git-rev-list -n 1 $com --pretty=format:%an | sed '1d')" \
|
32
|
+
-m "$(git-rev-list -n 1 $com --pretty=medium | sed '1,3d;s:^ ::')"
|
33
|
+
echo "DONE posting a comment to trac" | cat >>$LOG
|
34
|
+
done
|
35
|
+
done
|
36
|
+
|
37
|
+
echo "Done with trac commit hook: $TRAC_ENV" | cat >>$LOG
|
38
|
+
|
39
|
+
git reset --hard
|
40
|
+
echo "resetting repo working copy" | cat >>$LOG
|
@@ -0,0 +1,285 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
# trac-post-commit-hook
|
4
|
+
# ----------------------------------------------------------------------------
|
5
|
+
# Copyright (c) 2004 Stephen Hansen
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to
|
9
|
+
# deal in the Software without restriction, including without limitation the
|
10
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
11
|
+
# sell copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
20
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
22
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
23
|
+
# IN THE SOFTWARE.
|
24
|
+
# ----------------------------------------------------------------------------
|
25
|
+
|
26
|
+
### Changes for the Timing and Estimation plugin
|
27
|
+
#
|
28
|
+
# This script is very similar to "trac-post-commit" included with trac
|
29
|
+
# itself. This section explains functional changes relative to that
|
30
|
+
# script, and comments throughout the code explain other differences.
|
31
|
+
#
|
32
|
+
## Logging support for debugging this hook
|
33
|
+
#
|
34
|
+
## Support for specifying time spent in commit messages.
|
35
|
+
#
|
36
|
+
# "Blah refs #12 (1)" will add 1h to the spent time for issue #12
|
37
|
+
# "Blah refs #12 (spent 1.5)" will add 1h to the spent time for issue #12
|
38
|
+
#
|
39
|
+
# As above it is possible to use complicated messages:
|
40
|
+
#
|
41
|
+
# "Changed blah and foo to do this or that. Fixes #10 (1) and #12 (2),
|
42
|
+
# and refs #13 (0.5)."
|
43
|
+
#
|
44
|
+
# This will close #10 and #12, and add a note to #13 and also add 1h
|
45
|
+
# spent time to #10, add 2h spent time to #12 and add 30m spent time
|
46
|
+
# to #13.
|
47
|
+
#
|
48
|
+
# Note that:
|
49
|
+
# (spent 2), (sp 2) or simply (2) may be used for spent
|
50
|
+
# ' ', ',', '&' or 'and' may be used references
|
51
|
+
|
52
|
+
# This Subversion post-commit hook script is meant to interface to the
|
53
|
+
# Trac (http://www.edgewall.com/products/trac/) issue tracking/wiki/etc
|
54
|
+
# system.
|
55
|
+
#
|
56
|
+
# It should be called from the 'post-commit' script in Subversion, such as
|
57
|
+
# via:
|
58
|
+
#
|
59
|
+
# REPOS="$1"
|
60
|
+
# REV="$2"
|
61
|
+
# TRAC_ENV="/path/to/tracenv"
|
62
|
+
#
|
63
|
+
# /usr/bin/python /usr/local/src/trac/contrib/trac-post-commit-hook \
|
64
|
+
# -p "$TRAC_ENV" -r "$REV"
|
65
|
+
#
|
66
|
+
# (all the other arguments are now deprecated and not needed anymore)
|
67
|
+
#
|
68
|
+
# It searches commit messages for text in the form of:
|
69
|
+
# command #1
|
70
|
+
# command #1, #2
|
71
|
+
# command #1 & #2
|
72
|
+
# command #1 and #2
|
73
|
+
#
|
74
|
+
# Instead of the short-hand syntax "#1", "ticket:1" can be used as well, e.g.:
|
75
|
+
# command ticket:1
|
76
|
+
# command ticket:1, ticket:2
|
77
|
+
# command ticket:1 & ticket:2
|
78
|
+
# command ticket:1 and ticket:2
|
79
|
+
#
|
80
|
+
# In addition, the ':' character can be omitted and issue or bug can be used
|
81
|
+
# instead of ticket.
|
82
|
+
#
|
83
|
+
# You can have more then one command in a message. The following commands
|
84
|
+
# are supported. There is more then one spelling for each command, to make
|
85
|
+
# this as user-friendly as possible.
|
86
|
+
#
|
87
|
+
# close, closed, closes, fix, fixed, fixes
|
88
|
+
# The specified issue numbers are closed with the contents of this
|
89
|
+
# commit message being added to it.
|
90
|
+
# references, refs, addresses, re, see
|
91
|
+
# The specified issue numbers are left in their current status, but
|
92
|
+
# the contents of this commit message are added to their notes.
|
93
|
+
#
|
94
|
+
# A fairly complicated example of what you can do is with a commit message
|
95
|
+
# of:
|
96
|
+
#
|
97
|
+
# Changed blah and foo to do this or that. Fixes #10 and #12, and refs #12.
|
98
|
+
#
|
99
|
+
# This will close #10 and #12, and add a note to #12.
|
100
|
+
|
101
|
+
import re
|
102
|
+
import os
|
103
|
+
import sys
|
104
|
+
from datetime import datetime
|
105
|
+
from optparse import OptionParser
|
106
|
+
|
107
|
+
parser = OptionParser()
|
108
|
+
depr = '(not used anymore)'
|
109
|
+
parser.add_option('-e', '--require-envelope', dest='envelope', default='',
|
110
|
+
help="""
|
111
|
+
Require commands to be enclosed in an envelope.
|
112
|
+
If -e[], then commands must be in the form of [closes #4].
|
113
|
+
Must be two characters.""")
|
114
|
+
parser.add_option('-p', '--project', dest='project',
|
115
|
+
help='Path to the Trac project.')
|
116
|
+
parser.add_option('-r', '--revision', dest='rev',
|
117
|
+
help='Repository revision number.')
|
118
|
+
parser.add_option('-u', '--user', dest='user',
|
119
|
+
help='The user who is responsible for this action '+depr)
|
120
|
+
parser.add_option('-m', '--msg', dest='msg',
|
121
|
+
help='The log message to search '+depr)
|
122
|
+
parser.add_option('-c', '--encoding', dest='encoding',
|
123
|
+
help='The encoding used by the log message '+depr)
|
124
|
+
parser.add_option('-s', '--siteurl', dest='url',
|
125
|
+
help=depr+' the base_url from trac.ini will always be used.')
|
126
|
+
|
127
|
+
(options, args) = parser.parse_args(sys.argv[1:])
|
128
|
+
|
129
|
+
if not 'PYTHON_EGG_CACHE' in os.environ:
|
130
|
+
os.environ['PYTHON_EGG_CACHE'] = os.path.join(options.project, '.egg-cache')
|
131
|
+
|
132
|
+
from trac.env import open_environment
|
133
|
+
from trac.ticket.notification import TicketNotifyEmail
|
134
|
+
from trac.ticket import Ticket
|
135
|
+
from trac.ticket.web_ui import TicketModule
|
136
|
+
# TODO: move grouped_changelog_entries to model.py
|
137
|
+
from trac.util.text import to_unicode
|
138
|
+
from trac.util.datefmt import utc
|
139
|
+
from trac.versioncontrol.api import NoSuchChangeset
|
140
|
+
|
141
|
+
# Change logfile to point to someplace this script can write.
|
142
|
+
logfile = "/var/trac/commithook.log"
|
143
|
+
LOG = False
|
144
|
+
|
145
|
+
if LOG:
|
146
|
+
f = open (logfile,"w")
|
147
|
+
f.write("Begin Log\n")
|
148
|
+
f.close()
|
149
|
+
def log (s, *params):
|
150
|
+
f = open (logfile,"a")
|
151
|
+
f.write(s % params)
|
152
|
+
f.write("\n")
|
153
|
+
f.close()
|
154
|
+
else:
|
155
|
+
def log (s, *params):
|
156
|
+
pass
|
157
|
+
|
158
|
+
# Relative to trac standard, this table is hoisted out of class
|
159
|
+
# CommitHook so that it can be used in constructing a regexp that only
|
160
|
+
# matches on supported commands.
|
161
|
+
_supported_cmds = {'close': '_cmdClose',
|
162
|
+
'closed': '_cmdClose',
|
163
|
+
'closes': '_cmdClose',
|
164
|
+
'fix': '_cmdClose',
|
165
|
+
'fixed': '_cmdClose',
|
166
|
+
'fixes': '_cmdClose',
|
167
|
+
'addresses': '_cmdRefs',
|
168
|
+
're': '_cmdRefs',
|
169
|
+
'references': '_cmdRefs',
|
170
|
+
'refs': '_cmdRefs',
|
171
|
+
'see': '_cmdRefs'}
|
172
|
+
|
173
|
+
# Regexps are extended to include "(1)" and "(spent 1)".
|
174
|
+
ticket_prefix = '(?:#|(?:ticket|issue|bug)[: ]?)'
|
175
|
+
time_pattern = r'[ ]?(?:\((?:(?:spent|sp)[ ]?)?(-?[0-9]*(?:\.[0-9]+)?)\))?'
|
176
|
+
ticket_reference = ticket_prefix + '[0-9]+' + time_pattern
|
177
|
+
support_cmds_pattern = '|'.join(_supported_cmds.keys())
|
178
|
+
|
179
|
+
# Relative to upstream, only match command tokens (rather than
|
180
|
+
# matching all words).
|
181
|
+
ticket_command = (r'(?P<action>(?:%s))[ ]*'
|
182
|
+
'(?P<ticket>%s(?:(?:[, &]*|[ ]?and[ ]?)%s)*)' %
|
183
|
+
(support_cmds_pattern, ticket_reference, ticket_reference))
|
184
|
+
|
185
|
+
if options.envelope:
|
186
|
+
ticket_command = r'\%s%s\%s' % (options.envelope[0], ticket_command,
|
187
|
+
options.envelope[1])
|
188
|
+
|
189
|
+
# Because we build the regexp to recognize only supported commands,
|
190
|
+
# ignore case here.
|
191
|
+
command_re = re.compile(ticket_command, re.IGNORECASE)
|
192
|
+
ticket_re = re.compile(ticket_prefix + '([0-9]+)' + time_pattern, re.IGNORECASE)
|
193
|
+
|
194
|
+
class CommitHook:
|
195
|
+
def __init__(self, project=options.project, author=options.user,
|
196
|
+
rev=options.rev, url=options.url):
|
197
|
+
self.env = open_environment(project)
|
198
|
+
repos = self.env.get_repository()
|
199
|
+
repos.sync()
|
200
|
+
|
201
|
+
# Instead of bothering with the encoding, we'll use unicode data
|
202
|
+
# as provided by the Trac versioncontrol API (#1310).
|
203
|
+
try:
|
204
|
+
chgset = repos.get_changeset(rev)
|
205
|
+
except NoSuchChangeset:
|
206
|
+
return # out of scope changesets are not cached
|
207
|
+
self.author = chgset.author
|
208
|
+
self.rev = rev
|
209
|
+
self.msg = "(In [%s]) %s" % (rev, chgset.message)
|
210
|
+
self.now = datetime.now(utc)
|
211
|
+
|
212
|
+
cmd_groups = command_re.findall(self.msg)
|
213
|
+
|
214
|
+
log ("cmd_groups:%s", cmd_groups)
|
215
|
+
tickets = {}
|
216
|
+
# \todo Explain what xxx1 and xxx2 do; I can't see more params
|
217
|
+
# in command_re.
|
218
|
+
for cmd, tkts, xxx1, xxx2 in cmd_groups:
|
219
|
+
log ("cmd:%s, tkts%s ", cmd, tkts)
|
220
|
+
funcname = _supported_cmds.get(cmd.lower(), '')
|
221
|
+
if funcname:
|
222
|
+
for tkt_id, spent in ticket_re.findall(tkts):
|
223
|
+
func = getattr(self, funcname)
|
224
|
+
tickets.setdefault(tkt_id, []).append([func, spent])
|
225
|
+
|
226
|
+
for tkt_id, vals in tickets.iteritems():
|
227
|
+
log ("tkt_id:%s, vals%s ", tkt_id, vals)
|
228
|
+
spent_total = 0.0
|
229
|
+
try:
|
230
|
+
db = self.env.get_db_cnx()
|
231
|
+
|
232
|
+
ticket = Ticket(self.env, int(tkt_id), db)
|
233
|
+
for (cmd, spent) in vals:
|
234
|
+
cmd(ticket)
|
235
|
+
if spent:
|
236
|
+
spent_total += float(spent)
|
237
|
+
|
238
|
+
# determine sequence number...
|
239
|
+
cnum = 0
|
240
|
+
tm = TicketModule(self.env)
|
241
|
+
for change in tm.grouped_changelog_entries(ticket, db):
|
242
|
+
if change['permanent']:
|
243
|
+
cnum += 1
|
244
|
+
|
245
|
+
if spent_total:
|
246
|
+
self._setTimeTrackerFields(ticket, spent_total)
|
247
|
+
ticket.save_changes(self.author, self.msg, self.now, db, cnum+1)
|
248
|
+
db.commit()
|
249
|
+
|
250
|
+
tn = TicketNotifyEmail(self.env)
|
251
|
+
tn.notify(ticket, newticket=0, modtime=self.now)
|
252
|
+
except Exception, e:
|
253
|
+
# import traceback
|
254
|
+
# traceback.print_exc(file=sys.stderr)
|
255
|
+
log('Unexpected error while processing ticket ' \
|
256
|
+
'ID %s: %s' % (tkt_id, e))
|
257
|
+
print>>sys.stderr, 'Unexpected error while processing ticket ' \
|
258
|
+
'ID %s: %s' % (tkt_id, e)
|
259
|
+
|
260
|
+
|
261
|
+
def _cmdClose(self, ticket):
|
262
|
+
ticket['status'] = 'closed'
|
263
|
+
ticket['resolution'] = 'fixed'
|
264
|
+
|
265
|
+
def _cmdRefs(self, ticket):
|
266
|
+
pass
|
267
|
+
|
268
|
+
def _setTimeTrackerFields(self, ticket, spent):
|
269
|
+
log ("Setting ticket:%s spent: %s", ticket, spent)
|
270
|
+
if (spent != ''):
|
271
|
+
spentTime = float(spent)
|
272
|
+
# \bug If the ticket has not been modified since
|
273
|
+
# TimingAndEstimation was installed, then it might not
|
274
|
+
# have hours. It should still get hours applied because
|
275
|
+
# estimating and recording are separate.
|
276
|
+
if (ticket.values.has_key('hours')):
|
277
|
+
ticket['hours'] = str(spentTime)
|
278
|
+
|
279
|
+
if __name__ == "__main__":
|
280
|
+
if len(sys.argv) < 5:
|
281
|
+
print "For usage: %s --help" % (sys.argv[0])
|
282
|
+
print
|
283
|
+
print "Note that the deprecated options will be removed in Trac 0.12."
|
284
|
+
else:
|
285
|
+
CommitHook()
|