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.
- data/VERSION.yml +1 -1
- data/bin/subtrac +2 -0
- data/lib/subtrac.rb +85 -50
- data/lib/subtrac/config/config.yml +22 -19
- data/lib/subtrac/templates/location.erb +2 -2
- data/lib/subtrac/templates/projects/blank/trac/wiki/WikiStart +1 -1
- data/lib/subtrac/templates/trac.erb +1 -1
- data/lib/subtrac/templates/vhost.erb +6 -6
- metadata +1 -105
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/controller.py +0 -419
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.cfg +0 -3
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.py +0 -20
- data/lib/subtrac/trac-plugins/clientsplugin/clients/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action.py +0 -28
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action_email.py +0 -168
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action_zendesk_forum.py +0 -137
- data/lib/subtrac/trac-plugins/clientsplugin/clients/admin.py +0 -91
- data/lib/subtrac/trac-plugins/clientsplugin/clients/api.py +0 -199
- data/lib/subtrac/trac-plugins/clientsplugin/clients/client.py +0 -105
- data/lib/subtrac/trac-plugins/clientsplugin/clients/events.py +0 -287
- data/lib/subtrac/trac-plugins/clientsplugin/clients/eventsadmin.py +0 -71
- data/lib/subtrac/trac-plugins/clientsplugin/clients/htdocs/clients.css +0 -4
- data/lib/subtrac/trac-plugins/clientsplugin/clients/model.py +0 -135
- data/lib/subtrac/trac-plugins/clientsplugin/clients/processor.py +0 -70
- data/lib/subtrac/trac-plugins/clientsplugin/clients/reportmanager.py +0 -142
- data/lib/subtrac/trac-plugins/clientsplugin/clients/reports.py +0 -231
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary.py +0 -27
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_milestone.py +0 -152
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_ticketchanges.py +0 -160
- data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_client_events.html +0 -124
- data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_clients.html +0 -134
- data/lib/subtrac/trac-plugins/clientsplugin/cron/changes.xslt +0 -132
- data/lib/subtrac/trac-plugins/clientsplugin/cron/run-client-event +0 -97
- data/lib/subtrac/trac-plugins/clientsplugin/cron/summary.xslt +0 -161
- data/lib/subtrac/trac-plugins/clientsplugin/setup.py +0 -43
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/__init__.py +0 -4
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/burndownchart.py +0 -273
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursinplaceeditor.py +0 -44
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursremaining.py +0 -36
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery-1.2.3.min.js +0 -32
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.js +0 -409
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.mini.js +0 -30
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/templates/edithours.html +0 -53
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/burndownchart.py +0 -181
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/hoursremaining.py +0 -66
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/workloadchart.py +0 -47
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/utils.py +0 -93
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/workloadchart.py +0 -86
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/setup.py +0 -20
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/SumRollups.js +0 -23
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/adw_tracdb.py +0 -128
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/git-post-receive +0 -40
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac-post-commit.py +0 -285
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac_billing.py +0 -173
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/mail.py +0 -164
- data/lib/subtrac/trac-plugins/timingandestimationplugin/setup.py +0 -69
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/__init__.py +0 -1
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/api.py +0 -292
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/blackmagic.py +0 -172
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/dbhelper.py +0 -178
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/billingplugin.css +0 -25
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/field_disabler.js +0 -6
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/formatDate.js +0 -356
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_centerwindow.js +0 -100
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_followscroll.js +0 -84
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/wz_tooltip.js +0 -1149
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/linkifyer.js +0 -119
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/query.js +0 -73
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/ticket.js +0 -165
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/query_webui.py +0 -28
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reportmanager.py +0 -221
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports.py +0 -675
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports_filter.py +0 -150
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/statuses.py +0 -25
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/tande_filters.py +0 -131
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.cs +0 -84
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.html +0 -104
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_daemon.py +0 -194
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_policy.py +0 -62
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_webui.py +0 -28
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/usermanual.py +0 -127
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/webui.py +0 -129
- data/lib/subtrac/trac-plugins/worklogplugin/setup.py +0 -29
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/__init__.py +0 -1
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/api.py +0 -187
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.css +0 -40
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.js +0 -67
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.mousewheel.pack.js +0 -12
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.timeentry.pack.js +0 -7
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/tracWorklog.js +0 -40
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.css +0 -208
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.js +0 -1439
- 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 +0 -80
- 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 +0 -336
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/reports.py +0 -598
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog.html +0 -45
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_stop.html +0 -70
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_user.html +0 -40
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_webadminui.html +0 -59
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_daemon.py +0 -33
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_filter.py +0 -153
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/timeline_hook.py +0 -96
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/usermanual.py +0 -29
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/util.py +0 -31
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/webadminui.py +0 -47
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/webui.py +0 -174
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/xmlrpc.py +0 -73
@@ -1,27 +0,0 @@
|
|
1
|
-
from lxml import etree
|
2
|
-
from trac.core import *
|
3
|
-
|
4
|
-
class IClientSummaryProvider(Interface):
|
5
|
-
"""Extension point interface for components that define their own way
|
6
|
-
to summarise a given client.
|
7
|
-
"""
|
8
|
-
|
9
|
-
def get_name():
|
10
|
-
"""Return the name of the summary (for use in UI)
|
11
|
-
"""
|
12
|
-
|
13
|
-
def get_description():
|
14
|
-
"""Return the description of the summary (for use in UI)
|
15
|
-
"""
|
16
|
-
|
17
|
-
def options(client=None):
|
18
|
-
"""Return a series of tupoles defining the options
|
19
|
-
"""
|
20
|
-
|
21
|
-
def init(event, client):
|
22
|
-
"""Initialise the summary for a specific instance and client combo
|
23
|
-
"""
|
24
|
-
|
25
|
-
def get_summary(req, fromdate = None, todate = None):
|
26
|
-
"""Get the summary. This must return an etree object
|
27
|
-
"""
|
@@ -1,152 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
import os
|
3
|
-
import sys
|
4
|
-
import locale
|
5
|
-
import time
|
6
|
-
import codecs
|
7
|
-
from datetime import datetime
|
8
|
-
from optparse import OptionParser
|
9
|
-
from StringIO import StringIO
|
10
|
-
|
11
|
-
from trac.core import *
|
12
|
-
from trac.env import open_environment
|
13
|
-
from trac.util.datefmt import format_date, to_datetime
|
14
|
-
from trac.wiki import wiki_to_html
|
15
|
-
from genshi import escape
|
16
|
-
|
17
|
-
from lxml import etree
|
18
|
-
from clients.summary import IClientSummaryProvider
|
19
|
-
from clients.processor import extract_client_text
|
20
|
-
|
21
|
-
|
22
|
-
class ClientMilestoneSummary(Component):
|
23
|
-
implements(IClientSummaryProvider)
|
24
|
-
|
25
|
-
client = None
|
26
|
-
debug = False
|
27
|
-
|
28
|
-
def get_name(self):
|
29
|
-
return "Milestone Summary"
|
30
|
-
|
31
|
-
def get_description(self):
|
32
|
-
return "Provide a summary of all tickets within milestones that have completion dates set where at least one ticket for that client is not closed in that milestone"
|
33
|
-
|
34
|
-
def options(self, client=None):
|
35
|
-
return []
|
36
|
-
|
37
|
-
def init(self, event, client):
|
38
|
-
self.client = client
|
39
|
-
return True
|
40
|
-
|
41
|
-
def get_summary(self, req, fromdate = None, todate = None):
|
42
|
-
def myformat_date(dte):
|
43
|
-
if dte:
|
44
|
-
return format_date(dte, '%e %b %Y')
|
45
|
-
return 'No date set'
|
46
|
-
def myformat_hours(hrs):
|
47
|
-
from math import floor
|
48
|
-
if hrs:
|
49
|
-
hrs = float(hrs)
|
50
|
-
if 0 != hrs:
|
51
|
-
neg = False
|
52
|
-
if hrs < 0:
|
53
|
-
neg = True
|
54
|
-
hours *= -1
|
55
|
-
mins = floor((hrs - floor(hrs)) * 60)
|
56
|
-
str = ''
|
57
|
-
if neg:
|
58
|
-
str = '-'
|
59
|
-
if hrs:
|
60
|
-
str = "%s%sh" % (str, int(floor(hrs)))
|
61
|
-
if mins:
|
62
|
-
str = "%s %sm" % (str, int(mins))
|
63
|
-
return str;
|
64
|
-
return 'No estimate available'
|
65
|
-
|
66
|
-
client = self.client
|
67
|
-
xml = etree.Element('clientsplugin')
|
68
|
-
|
69
|
-
# Place basic client info here
|
70
|
-
xclient = etree.SubElement(xml, 'client')
|
71
|
-
etree.SubElement(xclient, 'name').text = client
|
72
|
-
if fromdate:
|
73
|
-
etree.SubElement(xclient, 'lastupdate').text = myformat_date(fromdate)
|
74
|
-
|
75
|
-
# Information about milestones
|
76
|
-
milestones = {}
|
77
|
-
xmilestones = etree.SubElement(xml, 'milestones')
|
78
|
-
|
79
|
-
db = self.env.get_db_cnx()
|
80
|
-
have_data = False
|
81
|
-
# Load in a summary of the client's tickets
|
82
|
-
sql = ("""\
|
83
|
-
SELECT t.id, t.summary, t.description, t.status, t.milestone,
|
84
|
-
m.due, m.completed, m.description AS mdesc,
|
85
|
-
tcust2.value AS estimatedhours
|
86
|
-
FROM ticket_custom AS tcust
|
87
|
-
INNER JOIN ticket AS t ON tcust.ticket=t.id
|
88
|
-
LEFT JOIN ticket_custom AS tcust2 ON t.id=tcust2.ticket AND tcust2.name='estimatedhours'
|
89
|
-
LEFT JOIN milestone m ON t.milestone=m.name
|
90
|
-
WHERE tcust.name = 'client'
|
91
|
-
AND tcust.value = %s
|
92
|
-
AND t.milestone IN (
|
93
|
-
SELECT DISTINCT st.milestone
|
94
|
-
FROM ticket_custom AS stcust
|
95
|
-
INNER JOIN ticket AS st ON stcust.ticket=st.id
|
96
|
-
INNER JOIN milestone AS sm ON st.milestone=sm.name
|
97
|
-
WHERE stcust.name = tcust.name
|
98
|
-
AND stcust.value = tcust.value
|
99
|
-
AND st.status != 'closed'
|
100
|
-
AND sm.due > 0)
|
101
|
-
""")
|
102
|
-
cur2 = db.cursor()
|
103
|
-
cur2.execute(sql, (client,))
|
104
|
-
xsummary = etree.SubElement(xml, 'summary')
|
105
|
-
for tid, summary, description, status, milestone, due, completed, mdescription, estimatedhours in cur2:
|
106
|
-
have_data = True
|
107
|
-
if milestone:
|
108
|
-
if not milestones.has_key(milestone):
|
109
|
-
xmilestone = etree.SubElement(xmilestones, 'milestone')
|
110
|
-
etree.SubElement(xmilestone, 'name').text = milestone
|
111
|
-
etree.SubElement(xmilestone, 'duetimestamp').text = str(due)
|
112
|
-
etree.SubElement(xmilestone, 'due').text = myformat_date(due)
|
113
|
-
if completed:
|
114
|
-
etree.SubElement(xmilestone, 'completed').text = myformat_date(completed)
|
115
|
-
if mdescription:
|
116
|
-
xmilestone.append(etree.XML('<description>%s</description>' % wiki_to_html(extract_client_text(mdescription), self.env, req)))
|
117
|
-
else:
|
118
|
-
etree.SubElement(xmilestone, 'description').text = ''
|
119
|
-
# Store for use
|
120
|
-
milestones[milestone] = { 'hours': 0, 'xml': xmilestone }
|
121
|
-
|
122
|
-
# Add hours to create a total.
|
123
|
-
if estimatedhours:
|
124
|
-
milestones[milestone]['hours'] += float(estimatedhours)
|
125
|
-
|
126
|
-
self.env.log.debug(" Summarising ticket #%s" % tid)
|
127
|
-
ticket = etree.SubElement(xsummary, 'ticket')
|
128
|
-
etree.SubElement(ticket, 'id').text = str(tid)
|
129
|
-
etree.SubElement(ticket, 'summary').text = summary
|
130
|
-
ticket.append(etree.XML('<description>%s</description>' % wiki_to_html(extract_client_text(description), self.env, req)))
|
131
|
-
etree.SubElement(ticket, 'status').text = status
|
132
|
-
etree.SubElement(ticket, 'milestone').text = milestone
|
133
|
-
# For conveneince, put the date here too (keeps the XSLTs simpler)
|
134
|
-
etree.SubElement(ticket, 'due').text = myformat_date(due)
|
135
|
-
if estimatedhours:
|
136
|
-
etree.SubElement(ticket, 'estimatedhours').text = myformat_hours(estimatedhours)
|
137
|
-
|
138
|
-
|
139
|
-
# Put the total hours into the milestone info
|
140
|
-
for milestone in milestones:
|
141
|
-
etree.SubElement(milestones[milestone]['xml'], 'estimatedhours').text = myformat_hours(milestones[milestone]['hours'])
|
142
|
-
|
143
|
-
if self.debug:
|
144
|
-
file = open('/tmp/send-client-email.xml', 'w')
|
145
|
-
file.write(etree.tostring(xml, pretty_print=True))
|
146
|
-
file.close()
|
147
|
-
self.env.log.debug(" Wrote XML to /tmp/send-client-email.xml")
|
148
|
-
|
149
|
-
if not have_data:
|
150
|
-
return None
|
151
|
-
|
152
|
-
return xml
|
@@ -1,160 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
import os
|
3
|
-
import sys
|
4
|
-
import locale
|
5
|
-
import time
|
6
|
-
import codecs
|
7
|
-
from datetime import datetime
|
8
|
-
from optparse import OptionParser
|
9
|
-
from StringIO import StringIO
|
10
|
-
|
11
|
-
from trac.core import *
|
12
|
-
from trac.env import open_environment
|
13
|
-
from trac.util.datefmt import format_date, to_datetime
|
14
|
-
from trac.wiki import wiki_to_html
|
15
|
-
from genshi import escape
|
16
|
-
|
17
|
-
from lxml import etree
|
18
|
-
from clients.summary import IClientSummaryProvider
|
19
|
-
from clients.processor import extract_client_text
|
20
|
-
|
21
|
-
|
22
|
-
class ClientTicketChanges(Component):
|
23
|
-
implements(IClientSummaryProvider)
|
24
|
-
|
25
|
-
client = None
|
26
|
-
debug = False
|
27
|
-
|
28
|
-
def get_name(self):
|
29
|
-
return "Ticket Change Summary"
|
30
|
-
|
31
|
-
def get_description(self):
|
32
|
-
return "Provide a summary of ticket changes since the last run"
|
33
|
-
|
34
|
-
def options(self, client=None):
|
35
|
-
return []
|
36
|
-
|
37
|
-
def init(self, event, client):
|
38
|
-
self.client = client
|
39
|
-
return True
|
40
|
-
|
41
|
-
def get_summary(self, req, fromdate = None, todate = None):
|
42
|
-
def myformat_date(dte):
|
43
|
-
if dte:
|
44
|
-
return format_date(dte, '%e %b %Y')
|
45
|
-
return 'No date set'
|
46
|
-
def myformat_hours(hrs):
|
47
|
-
from math import floor
|
48
|
-
if hrs:
|
49
|
-
hrs = float(hrs)
|
50
|
-
if 0 != hrs:
|
51
|
-
neg = False
|
52
|
-
if hrs < 0:
|
53
|
-
neg = True
|
54
|
-
hours *= -1
|
55
|
-
mins = floor((hrs - floor(hrs)) * 60)
|
56
|
-
str = ''
|
57
|
-
if neg:
|
58
|
-
str = '-'
|
59
|
-
if hrs:
|
60
|
-
str = "%s%sh" % (str, int(floor(hrs)))
|
61
|
-
if mins:
|
62
|
-
str = "%s %sm" % (str, int(mins))
|
63
|
-
return str;
|
64
|
-
return 'No estimate available'
|
65
|
-
|
66
|
-
client = self.client
|
67
|
-
xml = etree.Element('clientsplugin')
|
68
|
-
|
69
|
-
# Place basic client info here
|
70
|
-
xclient = etree.SubElement(xml, 'client')
|
71
|
-
etree.SubElement(xclient, 'name').text = client
|
72
|
-
if fromdate:
|
73
|
-
etree.SubElement(xclient, 'lastupdate').text = myformat_date(fromdate)
|
74
|
-
|
75
|
-
db = self.env.get_db_cnx()
|
76
|
-
have_data = False
|
77
|
-
# Load in any changes that have happend
|
78
|
-
sql = ("""\
|
79
|
-
SELECT t.id, t.summary, t.description, t.status, t.resolution, t.milestone, m.due,
|
80
|
-
tchng.field, tchng.oldvalue, tchng.newvalue
|
81
|
-
FROM ticket_custom tcust
|
82
|
-
INNER JOIN ticket AS t ON tcust.ticket=t.id
|
83
|
-
INNER JOIN ticket_change AS tchng ON t.id=tchng.ticket
|
84
|
-
LEFT JOIN milestone AS m ON t.milestone=m.name
|
85
|
-
WHERE tcust.name = 'client'
|
86
|
-
AND tcust.value = %s
|
87
|
-
AND tchng.field IN ('comment', 'status', 'resolution', 'milestone')
|
88
|
-
AND tchng.time >= %s
|
89
|
-
AND tchng.time < %s
|
90
|
-
AND t.milestone IN (
|
91
|
-
SELECT DISTINCT st.milestone
|
92
|
-
FROM ticket_custom AS stcust
|
93
|
-
INNER JOIN ticket AS st ON stcust.ticket=st.id
|
94
|
-
INNER JOIN milestone AS sm ON st.milestone=sm.name
|
95
|
-
WHERE stcust.name = tcust.name
|
96
|
-
AND stcust.value = tcust.value
|
97
|
-
AND sm.due > 0)
|
98
|
-
ORDER BY t.time
|
99
|
-
""")
|
100
|
-
cur2 = db.cursor()
|
101
|
-
cur2.execute(sql, (client, fromdate, todate))
|
102
|
-
changes = etree.SubElement(xml, 'changes')
|
103
|
-
lasttid = 0
|
104
|
-
for tid, summary, description, status, resolution, milestone, due, cgfield, oldvalue, newvalue in cur2:
|
105
|
-
text = ''
|
106
|
-
if 'status' == cgfield:
|
107
|
-
text = 'Status changed from "%s" to "%s"' % (oldvalue, newvalue)
|
108
|
-
elif 'milestone' == cgfield:
|
109
|
-
text = 'Milestone changed from "%s" to "%s" - please check for revised delivery date.' % (oldvalue, newvalue)
|
110
|
-
elif 'resolution' == cgfield:
|
111
|
-
if oldvalue and not newvalue:
|
112
|
-
text = 'Resolution removed'
|
113
|
-
elif not oldvalue and newvalue:
|
114
|
-
text = 'Resolution set to "%s"' % (newvalue)
|
115
|
-
else:
|
116
|
-
text = 'Resolution changed from "%s" to "%s"' % (oldvalue, newvalue)
|
117
|
-
elif 'comment' == cgfield:
|
118
|
-
# Todo - extract...
|
119
|
-
text = extract_client_text(newvalue).strip()
|
120
|
-
if '' == text:
|
121
|
-
# No comments for the client here so ignore it.
|
122
|
-
continue
|
123
|
-
text = "''Comment for your information:''[[BR]][[BR]]" + text
|
124
|
-
else:
|
125
|
-
# Client should not know any more than this
|
126
|
-
continue
|
127
|
-
|
128
|
-
if self.debug:
|
129
|
-
self.env.log.debug(" Change notification (%s) for ticket #%s" % (cgfield, tid))
|
130
|
-
have_data = True
|
131
|
-
if lasttid != tid:
|
132
|
-
ticket = etree.SubElement(changes, 'ticket')
|
133
|
-
etree.SubElement(ticket, 'id').text = str(tid)
|
134
|
-
etree.SubElement(ticket, 'summary').text = summary
|
135
|
-
ticket.append(etree.XML('<description>%s</description>' % wiki_to_html(extract_client_text(description), self.env, req)))
|
136
|
-
etree.SubElement(ticket, 'status').text = status
|
137
|
-
etree.SubElement(ticket, 'resolution').text = resolution
|
138
|
-
etree.SubElement(ticket, 'milestone').text = milestone
|
139
|
-
etree.SubElement(ticket, 'due').text = myformat_date(due)
|
140
|
-
changelog = etree.SubElement(ticket, 'changelog')
|
141
|
-
|
142
|
-
detail = etree.XML('<detail>%s</detail>' % wiki_to_html(text, self.env, req))
|
143
|
-
detail.set('field', cgfield)
|
144
|
-
if oldvalue:
|
145
|
-
detail.set('oldvalue', oldvalue)
|
146
|
-
if newvalue:
|
147
|
-
detail.set('newvalue', newvalue)
|
148
|
-
changelog.append(detail)
|
149
|
-
lasttid = tid
|
150
|
-
|
151
|
-
if self.debug:
|
152
|
-
file = open('/tmp/send-client-email.xml', 'w')
|
153
|
-
file.write(etree.tostring(xml, pretty_print=True))
|
154
|
-
file.close()
|
155
|
-
self.env.log.debug(" Wrote XML to /tmp/send-client-email.xml")
|
156
|
-
|
157
|
-
if not have_data:
|
158
|
-
return None
|
159
|
-
|
160
|
-
return xml
|
@@ -1,124 +0,0 @@
|
|
1
|
-
<!DOCTYPE html
|
2
|
-
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
-
<html xmlns="http://www.w3.org/1999/xhtml"
|
5
|
-
xmlns:xi="http://www.w3.org/2001/XInclude"
|
6
|
-
xmlns:py="http://genshi.edgewall.org/">
|
7
|
-
<xi:include href="admin.html" />
|
8
|
-
<head>
|
9
|
-
<title>Client Events</title>
|
10
|
-
<style type="text/css">
|
11
|
-
p.contextual { margin-top:0; margin-bottom:2em }
|
12
|
-
</style>
|
13
|
-
</head>
|
14
|
-
|
15
|
-
<body>
|
16
|
-
<h2>Manage Client Events</h2>
|
17
|
-
|
18
|
-
<py:choose test="view">
|
19
|
-
<form py:when="'detail'" class="mod" id="modclient" method="post">
|
20
|
-
<fieldset>
|
21
|
-
<legend>Event Overview:</legend>
|
22
|
-
<div class="field">
|
23
|
-
<label><b>Name:</b> $event.name</label>
|
24
|
-
<p class="help">Run this event using the <a href="http://trac-hacks.org/browser/clientsplugin/0.11/cron/run-client-event">run-client-event</a> script with the argument: -c "$event.name"</p>
|
25
|
-
</div>
|
26
|
-
<div class="field">
|
27
|
-
<label><b>Summary:</b> $event.summary</label>
|
28
|
-
<p class="help">$event.summary_description</p>
|
29
|
-
</div>
|
30
|
-
<div class="field">
|
31
|
-
<label><b>Action:</b> $event.action</label>
|
32
|
-
<p class="help">$event.action_description</p>
|
33
|
-
</div>
|
34
|
-
<div class="field">
|
35
|
-
<label><b>Last run:</b> ${pretty_timedelta(event.lastrun)} (${format_date(event.lastrun)} ${format_time(event.lastrun, str('%H:%M'))})</label>
|
36
|
-
</div>
|
37
|
-
</fieldset>
|
38
|
-
<py:for each="options in (event.summary_options, event.action_options)">
|
39
|
-
<fieldset py:if="options">
|
40
|
-
<py:with vars="field = options==event.summary_options and 'summary' or 'action'">
|
41
|
-
<legend py:if="'summary'==field">Summary Options</legend>
|
42
|
-
<legend py:if="'action'==field">Action Options</legend>
|
43
|
-
<div py:for="option in options.values()" class="field">
|
44
|
-
<label>$option.name</label><br />
|
45
|
-
<py:choose test="option.type">
|
46
|
-
<textarea py:when="'large'" name="${field}-option-${option.md5}" rows="10" cols="70">$option.value</textarea>
|
47
|
-
<input py:when="'small'" name="${field}-option-${option.md5}" type="text" size="5" value="$option.value" />
|
48
|
-
<select py:when="'list'" name="${field}-option-${option.md5}">
|
49
|
-
<option py:for="val in option.vals" selected="${val == option.value and 'selected' or None}">$val</option>
|
50
|
-
</select>
|
51
|
-
<input py:otherwise="" name="${field}-option-${option.md5}" type="text" size="60" value="$option.value" />
|
52
|
-
</py:choose>
|
53
|
-
<p class="help">$option.description</p>
|
54
|
-
</div>
|
55
|
-
</py:with>
|
56
|
-
</fieldset>
|
57
|
-
</py:for>
|
58
|
-
<div class="buttons">
|
59
|
-
<input type="submit" name="cancel" value="Cancel" />
|
60
|
-
<input type="submit" name="save" value="Save" />
|
61
|
-
</div>
|
62
|
-
</form>
|
63
|
-
|
64
|
-
<py:otherwise>
|
65
|
-
<form class="addnew" method="post">
|
66
|
-
<fieldset>
|
67
|
-
<legend>Add Client Event:</legend>
|
68
|
-
<div class="field">
|
69
|
-
<label>Name:<br /><input type="text" name="name" /></label>
|
70
|
-
</div>
|
71
|
-
<div class="field">
|
72
|
-
<label>Summary:<br />
|
73
|
-
<select name="summary">
|
74
|
-
<option py:for="summary in summaries">
|
75
|
-
$summary
|
76
|
-
</option>
|
77
|
-
</select>
|
78
|
-
</label>
|
79
|
-
</div>
|
80
|
-
<div class="field">
|
81
|
-
<label>Action:<br />
|
82
|
-
<select name="action">
|
83
|
-
<option py:for="action in actions">
|
84
|
-
$action
|
85
|
-
</option>
|
86
|
-
</select>
|
87
|
-
</label>
|
88
|
-
</div>
|
89
|
-
<div class="buttons">
|
90
|
-
<input type="submit" name="add" value="Add"/>
|
91
|
-
</div>
|
92
|
-
</fieldset>
|
93
|
-
</form>
|
94
|
-
|
95
|
-
<py:choose>
|
96
|
-
<form py:when="events" method="POST">
|
97
|
-
<table class="listing">
|
98
|
-
<thead>
|
99
|
-
<tr><th class="sel"> </th>
|
100
|
-
<th>Name</th><th>Summary</th><th>Action</th>
|
101
|
-
</tr>
|
102
|
-
</thead>
|
103
|
-
<tbody>
|
104
|
-
<tr py:for="event in events">
|
105
|
-
<td class="sel"><input type="checkbox" name="sel" value="$event.name" /></td>
|
106
|
-
<td class="name">
|
107
|
-
<a href="${panel_href(event.name)}">$event.name</a>
|
108
|
-
</td>
|
109
|
-
<td class="summary">$event.summary</td>
|
110
|
-
<td class="action">$event.action</td>
|
111
|
-
</tr>
|
112
|
-
</tbody>
|
113
|
-
</table>
|
114
|
-
<div class="buttons">
|
115
|
-
<input type="submit" name="remove" value="Remove selected items" />
|
116
|
-
<input type="submit" name="apply" value="Apply changes" />
|
117
|
-
</div>
|
118
|
-
</form>
|
119
|
-
</py:choose>
|
120
|
-
</py:otherwise>
|
121
|
-
</py:choose>
|
122
|
-
</body>
|
123
|
-
|
124
|
-
</html>
|