keithsalisbury-subtrac 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/VERSION.yml +1 -1
  2. data/bin/subtrac +2 -0
  3. data/lib/subtrac.rb +85 -50
  4. data/lib/subtrac/config/config.yml +22 -19
  5. data/lib/subtrac/templates/location.erb +2 -2
  6. data/lib/subtrac/templates/projects/blank/trac/wiki/WikiStart +1 -1
  7. data/lib/subtrac/templates/trac.erb +1 -1
  8. data/lib/subtrac/templates/vhost.erb +6 -6
  9. metadata +1 -105
  10. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/__init__.py +0 -0
  11. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/controller.py +0 -419
  12. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.cfg +0 -3
  13. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.py +0 -20
  14. data/lib/subtrac/trac-plugins/clientsplugin/clients/__init__.py +0 -0
  15. data/lib/subtrac/trac-plugins/clientsplugin/clients/action.py +0 -28
  16. data/lib/subtrac/trac-plugins/clientsplugin/clients/action_email.py +0 -168
  17. data/lib/subtrac/trac-plugins/clientsplugin/clients/action_zendesk_forum.py +0 -137
  18. data/lib/subtrac/trac-plugins/clientsplugin/clients/admin.py +0 -91
  19. data/lib/subtrac/trac-plugins/clientsplugin/clients/api.py +0 -199
  20. data/lib/subtrac/trac-plugins/clientsplugin/clients/client.py +0 -105
  21. data/lib/subtrac/trac-plugins/clientsplugin/clients/events.py +0 -287
  22. data/lib/subtrac/trac-plugins/clientsplugin/clients/eventsadmin.py +0 -71
  23. data/lib/subtrac/trac-plugins/clientsplugin/clients/htdocs/clients.css +0 -4
  24. data/lib/subtrac/trac-plugins/clientsplugin/clients/model.py +0 -135
  25. data/lib/subtrac/trac-plugins/clientsplugin/clients/processor.py +0 -70
  26. data/lib/subtrac/trac-plugins/clientsplugin/clients/reportmanager.py +0 -142
  27. data/lib/subtrac/trac-plugins/clientsplugin/clients/reports.py +0 -231
  28. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary.py +0 -27
  29. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_milestone.py +0 -152
  30. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_ticketchanges.py +0 -160
  31. data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_client_events.html +0 -124
  32. data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_clients.html +0 -134
  33. data/lib/subtrac/trac-plugins/clientsplugin/cron/changes.xslt +0 -132
  34. data/lib/subtrac/trac-plugins/clientsplugin/cron/run-client-event +0 -97
  35. data/lib/subtrac/trac-plugins/clientsplugin/cron/summary.xslt +0 -161
  36. data/lib/subtrac/trac-plugins/clientsplugin/setup.py +0 -43
  37. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/__init__.py +0 -4
  38. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/burndownchart.py +0 -273
  39. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursinplaceeditor.py +0 -44
  40. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursremaining.py +0 -36
  41. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery-1.2.3.min.js +0 -32
  42. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.js +0 -409
  43. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.mini.js +0 -30
  44. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/templates/edithours.html +0 -53
  45. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/burndownchart.py +0 -181
  46. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/hoursremaining.py +0 -66
  47. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/workloadchart.py +0 -47
  48. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/utils.py +0 -93
  49. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/workloadchart.py +0 -86
  50. data/lib/subtrac/trac-plugins/estimationtoolsplugin/setup.py +0 -20
  51. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/SumRollups.js +0 -23
  52. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/adw_tracdb.py +0 -128
  53. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/git-post-receive +0 -40
  54. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac-post-commit.py +0 -285
  55. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac_billing.py +0 -173
  56. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/__init__.py +0 -0
  57. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/mail.py +0 -164
  58. data/lib/subtrac/trac-plugins/timingandestimationplugin/setup.py +0 -69
  59. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/__init__.py +0 -1
  60. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/api.py +0 -292
  61. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/blackmagic.py +0 -172
  62. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/dbhelper.py +0 -178
  63. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/billingplugin.css +0 -25
  64. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/field_disabler.js +0 -6
  65. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/formatDate.js +0 -356
  66. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_centerwindow.js +0 -100
  67. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_followscroll.js +0 -84
  68. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/wz_tooltip.js +0 -1149
  69. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/linkifyer.js +0 -119
  70. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/query.js +0 -73
  71. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/ticket.js +0 -165
  72. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/query_webui.py +0 -28
  73. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reportmanager.py +0 -221
  74. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports.py +0 -675
  75. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports_filter.py +0 -150
  76. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/statuses.py +0 -25
  77. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/tande_filters.py +0 -131
  78. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.cs +0 -84
  79. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.html +0 -104
  80. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_daemon.py +0 -194
  81. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_policy.py +0 -62
  82. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_webui.py +0 -28
  83. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/usermanual.py +0 -127
  84. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/webui.py +0 -129
  85. data/lib/subtrac/trac-plugins/worklogplugin/setup.py +0 -29
  86. data/lib/subtrac/trac-plugins/worklogplugin/worklog/__init__.py +0 -1
  87. data/lib/subtrac/trac-plugins/worklogplugin/worklog/api.py +0 -187
  88. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.css +0 -40
  89. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.js +0 -67
  90. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.mousewheel.pack.js +0 -12
  91. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.timeentry.pack.js +0 -7
  92. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/tracWorklog.js +0 -40
  93. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.css +0 -208
  94. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.js +0 -1439
  95. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.png +0 -0
  96. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.xcf +0 -0
  97. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/worklogplugin.css +0 -80
  98. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstart.png +0 -0
  99. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstop.png +0 -0
  100. data/lib/subtrac/trac-plugins/worklogplugin/worklog/manager.py +0 -336
  101. data/lib/subtrac/trac-plugins/worklogplugin/worklog/reports.py +0 -598
  102. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog.html +0 -45
  103. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_stop.html +0 -70
  104. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_user.html +0 -40
  105. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_webadminui.html +0 -59
  106. data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_daemon.py +0 -33
  107. data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_filter.py +0 -153
  108. data/lib/subtrac/trac-plugins/worklogplugin/worklog/timeline_hook.py +0 -96
  109. data/lib/subtrac/trac-plugins/worklogplugin/worklog/usermanual.py +0 -29
  110. data/lib/subtrac/trac-plugins/worklogplugin/worklog/util.py +0 -31
  111. data/lib/subtrac/trac-plugins/worklogplugin/worklog/webadminui.py +0 -47
  112. data/lib/subtrac/trac-plugins/worklogplugin/worklog/webui.py +0 -174
  113. data/lib/subtrac/trac-plugins/worklogplugin/worklog/xmlrpc.py +0 -73
@@ -1,173 +0,0 @@
1
- from datetime import datetime as dt
2
- import time
3
- from utils import mail
4
- import adw_tracdb as db
5
-
6
- #defaultUrl= "https://sekhemt.acceleration.net/ADW/"
7
- _defaultUrl= "https://10.10.10.219/projects"
8
- _htmlLocation = '/var/BigVisibleCharts/Billing'
9
-
10
- def cond ( boolExpr, trueResult, falseResult ):
11
- """ This is the classic ?: operator from languages like C expressed in python (from dive into python)
12
- """
13
- return (boolExpr and [trueResult] or [falseResult])[0]
14
-
15
-
16
-
17
- def accumulator():
18
- var = [0]
19
- def fn(*params):
20
- for param in params:
21
- var[0] = var[0] + param
22
- return var[0]
23
- return fn
24
-
25
- def progn(*params):
26
- return params[-1]
27
-
28
- def prinTrue(s):
29
- print s
30
- return True
31
-
32
-
33
- def wn (name, attribs, *children):
34
- return "<%s %s>\n%s\n</%s>\n" % \
35
- (name, \
36
- ' '.join(['%s="%s"' % (key, val) for (key, val) in attribs.items()]), \
37
- '\n'.join([str(c) for c in children]), \
38
- name)
39
-
40
- def ticket_link (number, projUrl):
41
- return wn('a' , {'href':'/'.join([projUrl,'ticket' ,str(number)])}, "#"+str(number))
42
-
43
- def milestone_link (name, projUrl):
44
- name = str(name)
45
- return wn('a' , {'href':'/'.join([projUrl,'milestone' ,name])}, name)
46
-
47
- def make_project_output(project, rs, totalAcc):
48
- projAcc = accumulator()
49
- projLink = '/'.join([_defaultUrl, project ])
50
-
51
- def make_cell(idx, val):
52
- if(idx == rs.columnMap['ticket']):
53
- val = ticket_link(val, projLink)
54
- elif idx == rs.columnMap['milestone'] and val != '&nbsp;':
55
- val = milestone_link(val, projLink)
56
- elif idx == rs.columnMap['hours']:
57
- projAcc(float(val))
58
- totalAcc(float(val))
59
- return wn('td', {}, val)
60
-
61
- return progn(wn('div', {},
62
- wn('h2', {},
63
- wn('a',{'href':projLink},project)),
64
- wn('table', {"cellspacing":"0", "border":"1", "cellpadding":"3"},
65
- wn('tr', {},
66
- *[wn('th',{}, name) for name in rs.columnNames]),
67
- *[wn('tr', {},
68
- *[make_cell(idx, val)
69
- for idx in range(0, len(row))
70
- for val in [row[idx]]])
71
- for row in rs.rows]),
72
- wn('span',{}, "Total: "+str(projAcc()))))
73
-
74
- def make_all_projects_output():
75
- totalAcc = accumulator()
76
- sql = """
77
- SELECT
78
- CASE WHEN t.milestone IS NOT NULL and t.milestone <> '' THEN t.milestone
79
- ELSE '&nbsp;'
80
- END as milestone,
81
- t.id as ticket,
82
- SUM(newvalue) as hours,
83
- t.summary as summary,
84
- strftime('%m/%d/%Y %H:%M:%S', MAX(ticket_change.time), 'unixepoch', 'localtime') as [most-recent-update] ,
85
- (SELECT CASE WHEN MAX(time) IS NOT NULL THEN strftime('%m/%d/%Y %H:%M:%S', MAX(time), 'unixepoch', 'localtime')
86
- ELSE 'No previous bill date'
87
- END as time FROM bill_date ) as [previous-bill-date]
88
- FROM ticket as t
89
- LEFT JOIN ticket_custom as billable on billable.ticket = t.id
90
- AND billable.name = 'billable'
91
- JOIN ticket_change on t.id = ticket_change.ticket
92
- AND (
93
- ticket_change.time >
94
- (SELECT CASE WHEN MAX(time) IS NOT NULL THEN MAX(time)
95
- ELSE 0
96
- END as time FROM bill_date )
97
- )
98
- WHERE ticket_change.field = 'hours'
99
- AND billable.value=1
100
- GROUP BY t.milestone, t.id
101
- """
102
-
103
- billingInfo = db.collectResultsFromAllTracs(sql);
104
- projects_output = '\n'.join([make_project_output(project, rs, totalAcc)
105
- for (project, rs) in billingInfo
106
- if rs.rows ])
107
- return wn('html', {},
108
- wn('head', {}),
109
- wn('body', {},
110
- projects_output,
111
- wn('span',{},"Total hours billed: "+str(totalAcc()))))
112
-
113
-
114
- def save_output_to_file(output, when=0):
115
- if not when:
116
- when = dt.now()
117
- fname = '_'.join(["billing", str(when.year),
118
- str(when.month), str(when.day),
119
- str(when.hour), str(when.minute), ".html"])
120
- p = "/".join([_htmlLocation, fname])
121
- print "----"
122
- print "Writing out billing information to '%s'" % p
123
- print "----"
124
- f = open(p, "w")
125
- f.write(output)
126
- f.close();
127
-
128
-
129
-
130
- def run_billing(emails="ryan@acceleration.net", when=0):
131
- if not when:
132
- when = dt.now()
133
- date = '/'.join([str(when.month), str(when.day), str(when.year)])
134
-
135
- print "Collecting output..."
136
- output = make_all_projects_output()
137
- save_output_to_file(output, when)
138
- print "Emailing results to %s" % emails
139
- if emails:
140
- mail.mail(emails, 'Trac Billing - %s ' % date, output, html=True, fromEmail='trac-tickets@acceleration.net')
141
- return output
142
-
143
-
144
-
145
- def add_bill_date(project, username="Timing and Estimation Plugin", when=0):
146
- now = time.time()
147
- if not when:
148
- when = now
149
- when = int(when)
150
- now = int(now)
151
- sql = """
152
- INSERT INTO bill_date (time, set_when, str_value)
153
- VALUES (?, ?, strftime('%m/%d/%Y %H:%M:%S',?, 'unixepoch', 'localtime'))
154
- """
155
- db.executeNonQuery(project, sql, when, now, when)
156
-
157
- def mark_billing_date_in_all_projects(when=0 ):
158
- print "Marking the bill date on all projects."
159
- if not when:
160
- when = time.time()
161
- for project in db.projects:
162
- try:
163
- add_bill_date(project, "Timing and Estimation Plugin", when);
164
- print "%s Succeeded." % project
165
- except Exception, e:
166
- print "* %s failed: %s" % (project , e.args)
167
- print "Done marking bill dates"
168
-
169
-
170
-
171
-
172
-
173
-
@@ -1,164 +0,0 @@
1
- """
2
- Helper library to ease emailing.
3
- """
4
-
5
-
6
-
7
- import smtplib
8
- import email
9
- from email import Encoders
10
- from email.Utils import formatdate
11
- from email.Message import Message
12
- from email.MIMEAudio import MIMEAudio
13
- from email.MIMEBase import MIMEBase
14
- from email.MIMEMultipart import MIMEMultipart
15
- from email.MIMEImage import MIMEImage
16
- from email.MIMEText import MIMEText
17
- import mimetypes
18
-
19
- FROMEMAIL = 'ryan@acceleration.net'
20
- SERVER = 'mail.acceleration.net'
21
-
22
- def rawEmail(toList, msg, server=SERVER, fromEmail=FROMEMAIL):
23
-
24
- s = smtplib.SMTP(server)
25
- smtpresult = s.sendmail(fromEmail, toList, msg)
26
-
27
- if smtpresult:
28
- errstr = ""
29
- for recip in smtpresult.keys():
30
- errstr = """Could not delivery mail to: %s
31
-
32
- Server said: %s
33
- %s
34
-
35
- %s""" % (recip, smtpresult[recip][0], smtpresult[recip][1], errstr)
36
- raise smtplib.SMTPException, errstr
37
-
38
-
39
- def processTo(to):
40
- """
41
- helper function that processes a string or list of addresses.
42
-
43
- returns (list of addresses, comma-delimited string of addresses)
44
-
45
- The first is used for rawEmail, and the second is used when creating
46
- headers.
47
- """
48
- toList = []
49
- if type(to) is list:
50
- toList = to[:]
51
- to = ", ".join(to)
52
- else:
53
- toList = [to]
54
-
55
- return (toList, to)
56
-
57
- def mail(to, subject, message, html=False, fromEmail=FROMEMAIL, server=SERVER):
58
- """
59
- Simplifies the emailing process, sending plaintext emails.
60
-
61
- to: accepts a list of emails or a single email.
62
-
63
- returns nothing, or throws an smtplib.SMTPException if there is a problem.
64
- """
65
- toList, to = processTo(to)
66
-
67
- htmlHeader = '\n'
68
-
69
- if html:
70
- htmlHeader = 'Content-Type: text/html; charset=ISO-8859-1\n\n'
71
-
72
- msg = '''To: %s
73
- From: %s
74
- Subject: %s
75
- Date: %s
76
- %s
77
- %s
78
- ''' % (to, fromEmail, subject, formatdate(), htmlHeader, message)
79
-
80
- rawEmail(toList, msg, fromEmail=fromEmail, server=server)
81
-
82
-
83
- def sms_me(subject, message):
84
- " sends an email to ryan's phone "
85
- mail('sms-ryan@acceleration.net', subject, message)
86
-
87
- def emailHtml(to, subject, message):
88
- toList, to = processTo(to)
89
- msg = MIMEMultipart()
90
- msg['Subject'] = subject
91
- msg['To'] = to
92
- msg['From'] = fromEmail
93
- msg['Date'] = formatdate()
94
- msg.preamble = 'You are not using a MIME-aware reader\n'
95
- msg.epilogue = ''
96
-
97
- #add the main message
98
- msg.attach(MIMEText(message))
99
-
100
- rawEmail(toList, msg.as_string())
101
-
102
- def emailFile(to, subject, message, filePath, mimeType=None, fromEmail=FROMEMAIL, server=SERVER):
103
- """
104
- sends an email attachment to the given address or list of addesses.
105
-
106
- if the mimeType is not specified, it uses mimetypes.guess_type to determine it.
107
- """
108
- toList, to = processTo(to)
109
-
110
- msg = MIMEMultipart()
111
- msg['Subject'] = subject
112
- msg['To'] = to
113
- msg['From'] = fromEmail
114
- msg['Date'] = formatdate()
115
- msg.preamble = 'You are not using a MIME-aware reader\n'
116
- msg.epilogue = ''
117
-
118
- #add the main message
119
- msg.attach(MIMEText(message))
120
-
121
- if type(filePath) is list:
122
- for f in filePath:
123
- addFile(f, msg, mimeType)
124
- else:
125
- addFile(filePath, msg, mimeType)
126
-
127
- rawEmail(toList, msg.as_string(), server=server, fromEmail=fromEmail)
128
-
129
- def addFile(filePath, message, mimeType=None):
130
- if mimeType is None:
131
- mimeType = mimetypes.guess_type(filePath)[0]
132
-
133
- maintype, subtype = mimeType.split('/', 1)
134
-
135
- def processFactory(mimeRunner, openMethod):
136
- def proc(path, subtype):
137
- fp = open(path, openMethod)
138
- fileMsg = mimeRunner(fp.read(), _subtype = subtype)
139
- fp.close()
140
- return fileMsg
141
-
142
- return proc
143
-
144
- messageMaker = {'text':processFactory(MIMEText, 'r'),
145
- 'image':processFactory(MIMEImage, 'rb'),
146
- 'audio':processFactory(MIMEAudio, 'rb')}
147
-
148
-
149
-
150
- fMsg = None
151
- if messageMaker.has_key(maintype):
152
- fMsg = messageMaker[maintype](filePath, subtype)
153
- else:
154
- fp = open(filePath, 'rb')
155
- fMsg = MIMEBase(maintype, subtype)
156
- fMsg.set_payload(fp.read())
157
- fp.close()
158
- # Encode the payload using Base64
159
- Encoders.encode_base64(fMsg)
160
-
161
- if fMsg is not None:
162
- fMsg.add_header('Content-Disposition', 'attachment', filename=filePath)
163
- message.attach(fMsg)
164
-
@@ -1,69 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- from setuptools import setup
4
-
5
- PACKAGE = 'timingandestimationplugin'
6
-
7
- setup(name=PACKAGE,
8
- description='Plugin to make Trac support time estimation and tracking with permissions',
9
- keywords='trac plugin estimation timetracking permissions',
10
- version='0.7.8',
11
- url='http://www.trac-hacks.org/wiki/TimingAndEstimationPlugin',
12
- license='http://www.opensource.org/licenses/mit-license.php',
13
- author='Russ Tyndall at Acceleration.net',
14
- author_email='russ@acceleration.net',
15
- long_description="""
16
- This Trac 0.11 plugin provides support for Time estimation and tracking,
17
- and permissions to view and set those fields
18
-
19
- See http://trac-hacks.org/wiki/TimingAndEstimationPlugin for details.
20
- """,
21
- packages=[PACKAGE],
22
- package_data={PACKAGE : ['templates/*.html', 'htdocs/js/*', 'htdocs/*.css', 'htdocs/*.js']},
23
- entry_points={'trac.plugins': '%s = %s' % (PACKAGE, PACKAGE)})
24
-
25
-
26
- #### FINANCIAL CONTRIBUTERS ####
27
- #
28
- # Obsidian Software: http://www.obsidiansoft.com/
29
- # Enterprise Solutions for Functional Processor
30
- # Design Verification
31
- #
32
- ################################
33
-
34
- #### AUTHORS ####
35
- ## Primary Author:
36
- ## Russell Tyndall
37
- ## Acceleration.net
38
- ## russ@acceleration.net
39
- ## trac-hacks user: bobbysmith007
40
-
41
- ##
42
-
43
- ## Alessio Massaro
44
- ## trac-hacks user: masariello
45
- ## Helped Get Reports working in postgres
46
- ## and started moving toward generic work
47
- ## rather than hours
48
-
49
- ## kkurzweil@lulu.com
50
- ## helped postegresql db backend compatiblity
51
-
52
- ## jonas
53
- ## made it so that base_url was unnecessary
54
-
55
- ## Colin Guthrie
56
- ## trac-hacks user: coling
57
- ## Refactored the custom reports code to make it
58
- ## easy for other plugins to provide reports to
59
- ## compliment those provided by default
60
- ## Added Javascript that improves Ticket UI
61
-
62
- ## Dave Abrahams <dave@boost-consulting.com>
63
- ##
64
- ## Genshi filters to remove T&E reports from the
65
- ## standard reports page, where they display errors
66
-
67
- ## Greg Troxel
68
- ##
69
- ## Updated the post commit hooks to be inline with upstream trac
@@ -1,292 +0,0 @@
1
- import re
2
- import dbhelper
3
- import time
4
- from tande_filters import *
5
- from reports_filter import *
6
- from blackmagic import *
7
- from ticket_daemon import *
8
- from ticket_webui import *
9
- from usermanual import *
10
- from ticket_policy import *
11
- from trac.log import logger_factory
12
- from trac.ticket import ITicketChangeListener, Ticket
13
- from trac.core import *
14
- from trac.env import IEnvironmentSetupParticipant
15
- from trac.perm import IPermissionRequestor, PermissionSystem
16
- from webui import *
17
- from query_webui import *
18
- from reportmanager import CustomReportManager
19
- from statuses import *
20
- from reports import all_reports
21
- from sets import Set
22
-
23
- ## report columns
24
- ## id|author|title|query|description
25
-
26
- class TimeTrackingSetupParticipant(Component):
27
- """ This is the config that must be there for this plugin to work:
28
-
29
- [ticket-custom]
30
- totalhours = text
31
- totalhours.value = 0
32
- totalhours.label = Total Hours
33
-
34
- billable = checkbox
35
- billable.value = 1
36
- billable.label = Is this billable?
37
-
38
- hours = text
39
- hours.value = 0
40
- hours.label = Hours to Add
41
-
42
- estimatedhours = text
43
- estimatedhours.value = 0
44
- estimatedhours.label = Estimated Hours?
45
-
46
- internal = checkbox
47
- internal.value = 0
48
- internal.label = Internal?
49
-
50
- """
51
- implements(IEnvironmentSetupParticipant)
52
- db_version_key = None
53
- db_version = None
54
- db_installed_version = None
55
-
56
- """Extension point interface for components that need to participate in the
57
- creation and upgrading of Trac environments, for example to create
58
- additional database tables."""
59
- def __init__(self):
60
- # Setup logging
61
- self.statuses_key = 'T&E-statuses'
62
- self.db_version_key = 'TimingAndEstimationPlugin_Db_Version'
63
- self.db_version = 8
64
- # Initialise database schema version tracking.
65
- self.db_installed_version = dbhelper.get_system_value(self, \
66
- self.db_version_key) or 0
67
-
68
- def environment_created(self):
69
- """Called when a new Trac environment is created."""
70
- if self.environment_needs_upgrade(None):
71
- self.upgrade_environment(None)
72
-
73
-
74
- def system_needs_upgrade(self):
75
- return self.db_installed_version < self.db_version
76
-
77
- def do_db_upgrade(self):
78
- if self.db_installed_version < 1:
79
- print "Creating bill_date table"
80
- sql = """
81
- CREATE TABLE bill_date (
82
- time integer,
83
- set_when integer,
84
- str_value text
85
- );
86
- """
87
- dbhelper.execute_non_query(self, sql)
88
-
89
-
90
- if self.db_installed_version < 5:
91
- if dbhelper.db_table_exists(self, 'report_version'):
92
- print "Dropping report_version table"
93
- sql = "DELETE FROM report " \
94
- "WHERE author=%s AND id IN (SELECT report FROM report_version)"
95
- dbhelper.execute_non_query(self, sql, 'Timing and Estimation Plugin')
96
-
97
- sql = "DROP TABLE report_version"
98
- dbhelper.execute_non_query(self, sql)
99
-
100
- #version 6 upgraded reports
101
-
102
-
103
- if self.db_installed_version < 7:
104
- field_settings = "field settings"
105
- self.config.set( field_settings, "fields", "billable, totalhours, hours, estimatedhours, internal" )
106
- self.config.set( field_settings, "billable.permission", "TIME_VIEW:hide, TIME_RECORD:disable" )
107
- self.config.set( field_settings, "hours.permission", "TIME_VIEW:remove, TIME_RECORD:disable" )
108
- self.config.set( field_settings, "estimatedhours.permission", "TIME_RECORD:disable" )
109
- self.config.set( field_settings, "internal.permission", "TIME_RECORD:hide")
110
-
111
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
112
- # This statement block always goes at the end this method
113
- dbhelper.set_system_value(self, self.db_version_key, self.db_version)
114
- self.db_installed_version = self.db_version
115
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
116
-
117
-
118
-
119
- def reports_need_upgrade(self):
120
- mgr = CustomReportManager(self.env, self.log)
121
- db_reports = mgr.get_version_hash_by_group(CustomReportManager.TimingAndEstimationKey)
122
- py_reports = {}
123
- for report_group in all_reports:
124
- for report in report_group['reports']:
125
- py_reports[report['uuid']]= report['version']
126
-
127
- diff = [(uuid, version) for (uuid, version) in py_reports.items()
128
- if not db_reports.has_key(uuid) or int(db_reports[uuid]) < int(version)]
129
-
130
- if len(diff) > 0:
131
- self.log.debug ("T&E needs upgrades for the following reports: %s" %
132
- (diff, ))
133
- return len(diff) > 0
134
-
135
- def do_reports_upgrade(self, force=False):
136
- self.log.debug( "Beginning Reports Upgrade");
137
- mgr = CustomReportManager(self.env, self.log)
138
- statuses = get_statuses(self)
139
- stat_vars = status_variables(statuses)
140
-
141
- for report_group in all_reports:
142
- rlist = report_group["reports"]
143
- group_title = report_group["title"]
144
- for report in rlist:
145
- title = report["title"]
146
- new_version = report["version"]
147
-
148
- sql = report["sql"].replace('#STATUSES#', stat_vars)
149
- mgr.add_report(report["title"], "Timing and Estimation Plugin", \
150
- "Reports Must Be Accessed From the Management Screen",
151
- sql, report["uuid"], report["version"],
152
- CustomReportManager.TimingAndEstimationKey,
153
- group_title, force)
154
-
155
- def ticket_fields_need_upgrade(self):
156
- ticket_custom = "ticket-custom"
157
- return not ( self.config.get( ticket_custom, "totalhours" ) and \
158
- self.config.get( ticket_custom, "hours" ) and \
159
- self.config.get( ticket_custom, "totalhours.order") and \
160
- self.config.get( ticket_custom, "hours.order") and \
161
- self.config.get( ticket_custom, "estimatedhours.order") and \
162
- self.config.get( ticket_custom, "estimatedhours") and \
163
- self.config.get( ticket_custom, "internal") and \
164
- "InternalTicketsPolicy" in self.config.getlist("trac", "permission_policies"))
165
-
166
- def do_ticket_field_upgrade(self):
167
- ticket_custom = "ticket-custom"
168
-
169
- if not self.config.get(ticket_custom,"totalhours"):
170
- self.config.set(ticket_custom,"totalhours", "text")
171
- self.config.set(ticket_custom,"totalhours.order", "4")
172
- self.config.set(ticket_custom,"totalhours.value", "0")
173
- self.config.set(ticket_custom,"totalhours.label", "Total Hours")
174
-
175
-
176
- if not self.config.get(ticket_custom,"billable"):
177
- self.config.set(ticket_custom,"billable", "checkbox")
178
- self.config.set(ticket_custom,"billable.value", "1")
179
- self.config.set(ticket_custom,"billable.order", "3")
180
- self.config.set(ticket_custom,"billable.label", "Billable?")
181
-
182
- if not self.config.get(ticket_custom,"hours"):
183
- self.config.set(ticket_custom,"hours", "text")
184
- self.config.set(ticket_custom,"hours.value", "0")
185
- self.config.set(ticket_custom,"hours.order", "2")
186
- self.config.set(ticket_custom,"hours.label", "Add Hours to Ticket")
187
-
188
- if not self.config.get(ticket_custom,"estimatedhours"):
189
- self.config.set(ticket_custom,"estimatedhours", "text")
190
- self.config.set(ticket_custom,"estimatedhours.value", "0")
191
- self.config.set(ticket_custom,"estimatedhours.order", "1")
192
- self.config.set(ticket_custom,"estimatedhours.label", "Estimated Number of Hours")
193
-
194
- if not self.config.get( ticket_custom, "internal"):
195
- self.config.set(ticket_custom, "internal", "checkbox")
196
- self.config.set(ticket_custom, "internal.value", "0")
197
- self.config.set(ticket_custom, "internal.label", "Internal?")
198
- self.config.set(ticket_custom,"internal.order", "5")
199
-
200
- if "InternalTicketsPolicy" not in self.config.getlist("trac", "permission_policies"):
201
- perms = ["InternalTicketsPolicy"]
202
- other_policies = self.config.getlist("trac", "permission_policies")
203
- if "DefaultPermissionPolicy" not in other_policies:
204
- perms.append("DefaultPermissionPolicy")
205
- perms.extend( other_policies )
206
- self.config.set("trac", "permission_policies", ', '.join(perms))
207
-
208
- self.config.save();
209
-
210
- def needs_user_man(self):
211
- maxversion = dbhelper.get_scalar(self, "SELECT MAX(version) FROM wiki WHERE name like %s", 0,
212
- user_manual_wiki_title)
213
- if (not maxversion) or maxversion < user_manual_version:
214
- return True
215
- return False
216
-
217
- def do_user_man_update(self):
218
-
219
- when = int(time.time())
220
- sql = """
221
- INSERT INTO wiki (name,version,time,author,ipnr,text,comment,readonly)
222
- VALUES ( %s, %s, %s, 'Timing and Estimation Plugin', '127.0.0.1', %s,'',0)
223
- """
224
- dbhelper.execute_non_query(self, sql,
225
- user_manual_wiki_title,
226
- user_manual_version,
227
- when,
228
- user_manual_content)
229
-
230
-
231
- def environment_needs_upgrade(self, db):
232
- """Called when Trac checks whether the environment needs to be upgraded.
233
-
234
- Should return `True` if this participant needs an upgrade to be
235
- performed, `False` otherwise.
236
-
237
- """
238
- self.log.debug("NEEDS UP?: sys:%s, rep:%s, stats:%s, fields:%s, man:%s" % \
239
- ((self.system_needs_upgrade()),
240
- (self.reports_need_upgrade()),
241
- (self.have_statuses_changed()),
242
- (self.ticket_fields_need_upgrade()),
243
- (self.needs_user_man())))
244
- return (self.system_needs_upgrade()) or \
245
- (self.reports_need_upgrade()) or \
246
- (self.have_statuses_changed()) or \
247
- (self.ticket_fields_need_upgrade()) or \
248
- (self.needs_user_man())
249
-
250
- def upgrade_environment(self, db):
251
- """Actually perform an environment upgrade.
252
-
253
- Implementations of this method should not commit any database
254
- transactions. This is done implicitly after all participants have
255
- performed the upgrades they need without an error being raised.
256
- """
257
- def p(s):
258
- print s
259
- return True
260
- print "Timing and Estimation needs an upgrade"
261
- p("Upgrading Database")
262
- self.do_db_upgrade()
263
- p("Upgrading reports")
264
- self.do_reports_upgrade(force=self.have_statuses_changed())
265
-
266
- #make sure we upgrade the statuses string so that we dont need to always rebuild the
267
- # reports
268
- stats = get_statuses(self)
269
- val = ','.join(list(stats))
270
- dbhelper.set_system_value(self, self.statuses_key, val)
271
-
272
- if self.ticket_fields_need_upgrade():
273
- p("Upgrading fields")
274
- self.do_ticket_field_upgrade()
275
- if self.needs_user_man():
276
- p("Upgrading usermanual")
277
- self.do_user_man_update()
278
- print "Done Upgrading"
279
-
280
- def have_statuses_changed(self):
281
- """get the statuses from the last time we saved them,
282
- compare them to the ones we have now (ignoring '' and None),
283
- if we have different ones, throw return true
284
- """
285
- s = dbhelper.get_system_value(self, self.statuses_key)
286
- if not s:
287
- return True
288
- sys_stats = get_statuses(self)
289
- s = s.split(',')
290
- sys_stats.symmetric_difference_update(s)
291
- sys_stats.difference_update(['', None])
292
- return len(sys_stats) > 0