Modify ↓
Opened 12 years ago
#11549 new enhancement
introduced fields.remain in tracpm.py
| Reported by: | falkb | Owned by: | Chris Nelson |
|---|---|---|---|
| Priority: | normal | Component: | TracJsGanttPlugin |
| Severity: | normal | Keywords: | |
| Cc: | Trac Release: |
Description
I use Trac with these additional custom ticket fields:
- estimatedhours.label = originally planned time
- remaininghours.label = currently estimated rest time
- totalhours.label = sum of worked time
I set in trac.ini
[TracPM] fields.estimate = estimatedhours fields.worked = totalhours
but this patch allows me setting also the remaininghours for the Gantt chart to consider:
[TracPM] fields.estimate = estimatedhours fields.worked = totalhours fields.remain = remaininghours
The patch is here:
!diff
Index: tracpm.py
===================================================================
--- tracpm.py (revision 13386)
+++ tracpm.py (working copy)
@@ -150,6 +150,8 @@
"""Ticket field to use as the data source for estimated work""")
Option(cfgSection, 'fields.worked', None,
"""Ticket field to use as the data source for completed work""")
+ Option(cfgSection, 'fields.remain', None,
+ """Ticket field to use as the data source for remaining work""")
Option(cfgSection, 'fields.start', None,
"""Ticket field to use as the data source for start date""")
Option(cfgSection, 'fields.finish', None,
@@ -187,7 +189,7 @@
self.sources = {}
# All the data sources that may come from custom fields.
- fields = ('percent', 'estimate', 'worked', 'start', 'finish',
+ fields = ('percent', 'estimate', 'worked', 'remain', 'start', 'finish',
'pred', 'succ', 'parent')
# All the data sources that may come from external relations.
@@ -501,11 +503,11 @@
est = 0.0
# Closed tickets took as long as they took
elif ticket['status'] == 'closed' and work:
- est = work
+ est = 0 # work
# If the task is over its estimate, assume it will take
# pad more time
- elif work > est:
- est = work + self.estPad
+ #elif work > est:
+ # est = work + self.estPad
# If unestimated, use the default
elif not est or est == 0:
est = self.dftEst
@@ -540,7 +542,9 @@
percent = 0
else:
worked = float(worked)
- percent = '%s/%s' % (worked, estimate)
+ prcnt = worked * 100.0 / (float(estimate) + worked)
+ percent = int(prcnt)
+ #self.env.log.info('#### percent(%s) = (worked=%d, estimate=%d) : %d' % (ticket['id'], worked, estimate, percent))
# Use percent if provided
elif self.isCfg('percent'):
try:
@@ -794,6 +798,9 @@
if self.isCfg('worked'):
ticket[self.fields['worked']] = 0
+ if self.isCfg('remain'):
+ ticket[self.fields['remain']] = 0
+
# There is no percent complete for a pseudoticket
if self.isCfg('percent'):
ticket[self.fields['percent']] = 0
@@ -820,11 +827,11 @@
else:
milestones = []
- for t in tickets:
- if 'milestone' in t and \
- t['milestone'] != '' and \
- t['milestone'] not in milestones:
- milestones.append(t['milestone'])
+ for t in tickets:
+ if 'milestone' in t and \
+ t['milestone'] != '' and \
+ t['milestone'] not in milestones:
+ milestones.append(t['milestone'])
# Need a unique ID for each task.
if len(milestones) > 0:
@@ -1008,7 +1015,7 @@
nullable = [ 'pred', 'succ',
'start', 'finish',
'parent',
- 'worked', 'estimate', 'percent' ]
+ 'worked', 'estimate', 'remain', 'percent' ]
for field in nullable:
if self.isField(field):
fieldName = self.fields[self.sources[field]]
@@ -1841,7 +1848,12 @@
# Set the field
t['_calc_finish'] = finish
+ start_based_on_work = False
+ overruled_start = None
+ start = None
+
if t.get('_calc_start') == None:
+ hours = 0
# If work has begun, the start is the actual start.
if t.get('_actual_start') and options.get('useActuals'):
start = [ to_datetime(t['_actual_start']), True ]
@@ -1856,6 +1868,7 @@
# Otherwise, the start is based on the finish and the
# work to be done before then.
else:
+ start_based_on_work = True
hours = self.pm.workHours(t)
start = t['_calc_finish'][0] + \
_calendarOffset(t,
@@ -1865,9 +1878,27 @@
t['_calc_start'] = start
+ if start_based_on_work:
+ if self.pm.isCfg('remain') and t.get(self.pm.fields['remain']) != None:
+ remain_str = t[self.pm.fields['remain']]
+ if remain_str != '':
+ remaininghours = float(remain_str)
+ else:
+ remaininghours = 0.0
+
+ start = t['_calc_finish'][0] + \
+ _calendarOffset(t,
+ -1*(remaininghours),
+ t['_calc_finish'][0])
+ start = [start, t['_calc_finish'][1]]
+ overruled_start = start
+ #t['_calc_start'] = overruled_start
+
# Adjust implicit finish for explicit start
if _betterDate(start, finish):
hours = self.pm.workHours(t)
+ if (overruled_start):
+ hours = remaining_hours
finish[0] = start[0] + _calendarOffset(t,
hours,
start[0])
@@ -1876,13 +1907,19 @@
# Remember the limit for open tickets
if t['status'] != 'closed':
+ if start is None:
+ start = t['_calc_start']
limit = self.limits.get(t['owner'])
- if not limit or limit > t['_calc_start'][0]:
- self.limits[t['owner']] = t['_calc_start'][0]
+ if not limit or limit > start[0]:
+ self.limits[t['owner']] = start[0]
self.taskStack.pop()
- return t['_calc_start']
+ if overruled_start:
+ #self.env.log.warning('ticket=%s' % t['id'])
+ return overruled_start
+ else:
+ return t['_calc_start']
# Schedule a task As Soon As Possible
# Return the finish of the task as a date object
Now the length of a ticket bar is equal to 'estimatedhours' but tickets can overlap because resource leveling will consider the 'remaininghours'.
Remaining issue: For proper ressource leveling, I don't get it work that closed tickets have the original bar length of 'estimatedhours'.
Attachments (0)
Note: See
TracTickets for help on using
tickets.


