Class Index [+]

Quicksearch

TaskJuggler::TimeSheetReport

This specialization of ReportBase implements a template generator for time sheets. The time sheet is structured using the TJP file syntax.

Public Class Methods

new(report) click to toggle source

Create a new object and set some default values.

    # File lib/taskjuggler/reports/TimeSheetReport.rb, line 53
53:     def initialize(report)
54:       super(report)
55: 
56:       @current = []
57:       @future = []
58:     end

Public Instance Methods

generateIntermediateFormat() click to toggle source

In the future we might want to generate other output than TJP synatx. So we generate an abstract version of the time sheet first. This abstract version has a TSResourceRecord for each resource and each of these records holds a TSTaskRecord for each assigned task.

    # File lib/taskjuggler/reports/TimeSheetReport.rb, line 64
64:     def generateIntermediateFormat
65:       super
66:       @current = collectRecords(a('start'), a('end'))
67:       newEnd = a('end') + (a('end').to_i - a('start').to_i)
68:       newEnd = @project['end'] if newEnd > @project['end']
69:       @future = collectRecords(a('end'), a('end') + (a('end') - a('start')))
70:     end
to_tjp() click to toggle source

Generate a time sheet in TJP syntax format.

     # File lib/taskjuggler/reports/TimeSheetReport.rb, line 73
 73:     def to_tjp
 74:       # This String will hold the result.
 75:       @file = # The status headline should be no more than 60 characters and may# not be empty! The status summary is optional and should be no# longer than one or two sentences of plain text. The details section# is also optional has no length limitation. You can use simple# markup in this section.  It is recommended that you provide at# least a summary or a details section.# See http://www.taskjuggler.org/tj3/manual/timesheet.html for details.## --------8<--------8<--------
 76: 
 77:       # Iterate over all the resources that we have TSResourceRecords for.
 78:       @current.each do |rr|
 79:         resource = rr.resource
 80:         # Generate the time sheet header
 81:         @file << "timesheet #{resource.fullId} " +
 82:                  "#{a('start')} - #{a('end')} {\n\n"
 83: 
 84:         @file << "  # Vacation time: #{rr.vacationPercent}%\n\n"
 85: 
 86:         if rr.tasks.empty?
 87:           # If there were no assignments, just write a comment.
 88:           @file << "  # There were no planned tasks assignments for " +
 89:                    "this period!\n\n"
 90:         else
 91:           rr.tasks.each do |tr|
 92:             task = tr.task
 93: 
 94:             @file << "  # Task: #{task.name}\n"
 95:             @file << "  task #{task.fullId} {\n"
 96:             #@file << "    work #{tr.workDays *
 97:             #                     @project['dailyworkinghours']}h\n"
 98:             @file << "    work #{tr.workPercent}%\n"
 99:             if tr.remaining
100:               @file << "    remaining #{tr.remaining}d\n"
101:             else
102:               @file << "    end #{tr.endDate}\n"
103:             end
104:             c = tr.workDays > 1.0 ? '' : '# '
105:             @file << "    #{c}status green \"Your headline here!\" {\n" +
106:                      "    #  summary -8<-\n" +
107:                      "    #  A summary text\n" +
108:                      "    #  ->8-\n" +
109:                      "    #  details -8<-\n" +
110:                      "    #  Some more details\n" +
111:                      "    #  ->8-\n" +
112:                      "    #  flags ...\n" +
113:                      "    #{c}}\n"
114:             @file << "  }\n\n"
115:           end
116:         end
117:         @file <<   # If you had unplanned tasks, uncomment and fill out the  # following lines:  # newtask new.task.id "A task title" {  #   work X%  #   remaining Y.Yd  #   status green "Your headline here!" {  #     summary -8<-  #     A summary text  #     ->8-  #     details -8<-  #     Some more details  #     ->8-  #     flags ...  #   }  # }  # You can use the following section to report personal notes.  # status green "Your headline here!" {  #   summary -8<-  #   A summary text  #   ->8-  #   details -8<-  #   Some more details  #   ->8-  # }
118:         future = @future[@future.index { |r| r.resource == resource }]
119:         if future && !future.tasks.empty?
120:           @file <<   #  # Your upcoming tasks for the next period  # Please check them carefully and discuss any necessary  # changes with your manager or project manager!  #
121:           future.tasks.each do |taskRecord|
122:             @file << "  # #{taskRecord.task.name}: #{taskRecord.workPercent}%\n"
123:           end
124:           @file << "\n"
125:         else
126:           @file << "\n  # You have no future assignments for this project!\n"
127:         end
128:         @file << "}\n# -------->8-------->8--------\n\n"
129:       end
130:       @file
131:     end

Private Instance Methods

collectRecords(from, to) click to toggle source
     # File lib/taskjuggler/reports/TimeSheetReport.rb, line 177
177:     def collectRecords(from, to)
178:       # Prepare the resource list.
179:       resourceList = PropertyList.new(@project.resources)
180:       resourceList.setSorting(@report.get('sortResources'))
181:       resourceList = filterResourceList(resourceList, nil,
182:                                         @report.get('hideResource'),
183:                                         @report.get('rollupResource'),
184:                                         @report.get('openNodes'))
185:       # Prepare a template for the Query we will use to get all the data.
186:       scenarioIdx = a('scenarios')[0]
187:       queryAttrs = { 'project' => @project,
188:                      'scopeProperty' => nil,
189:                      'scenarioIdx' => scenarioIdx,
190:                      'loadUnit' => a('loadUnit'),
191:                      'numberFormat' => a('numberFormat'),
192:                      'timeFormat' => a('timeFormat'),
193:                      'currencyFormat' => a('currencyFormat'),
194:                      'start' => from, 'end' => to,
195:                      'hideJournalEntry' => a('hideJournalEntry'),
196:                      'costAccount' => a('costAccount'),
197:                      'revenueAccount' => a('revenueAccount') }
198:       resourceList.query = Query.new(queryAttrs)
199:       resourceList.sort!
200: 
201:       # Prepare the task list.
202:       taskList = PropertyList.new(@project.tasks)
203:       taskList.setSorting(@report.get('sortTasks'))
204:       taskList = filterTaskList(taskList, nil, @report.get('hideTask'),
205:                                 @report.get('rollupTask'),
206:                                 @report.get('openNodes'))
207: 
208:       records = []
209:       resourceList.each do |resource|
210:         # Time sheets only make sense for leaf resources that actuall do work.
211:         next unless resource.leaf?
212: 
213:         # Create a new TSResourceRecord for the resource.
214:         records << (resourceRecord = TSResourceRecord.new(resource))
215: 
216:         # Calculate the average working days per week (usually 5)
217:         weeklyWorkingDays = @project['yearlyworkingdays'] / 52.1428
218:         # Calculate the number of weeks in the report
219:         weeksToReport = (to - from) / (60 * 60 * 24 * 7)
220: 
221:         # Get the vacation days for the resource for this period.
222:         queryAttrs['property'] = resource
223:         query = Query.new(queryAttrs)
224:         query.attributeId = 'vacationdays'
225:         query.start = from
226:         query.end = to
227:         query.process
228:         resourceRecord.vacationHours = query.to_s
229:         resourceRecord.vacationPercent =
230:           (query.to_num / (weeksToReport * weeklyWorkingDays)) * 100.0
231: 
232: 
233:         # Now we have to find all the task that the resource is allocated to
234:         # during the report period.
235:         assignedTaskList = filterTaskList(taskList, resource,
236:                                           a('hideTask'), a('rollupTask'),
237:                                           a('openNodes'))
238:         queryAttrs['scopeProperty'] = resource
239:         assignedTaskList.query = Query.new(queryAttrs)
240:         assignedTaskList.sort!
241: 
242:         assignedTaskList.each do |task|
243:           # Time sheet task records only make sense for leaf tasks.
244:           reportIv = Interval.new(from, to)
245:           taskIv = Interval.new(task['start', scenarioIdx],
246:                                 task['end', scenarioIdx])
247:           next if !task.leaf? || !reportIv.overlaps?(taskIv)
248: 
249:           queryAttrs['property'] = task
250:           query = Query.new(queryAttrs)
251: 
252:           # Get the allocated effort for the task for this period.
253:           query.attributeId = 'effort'
254:           query.start = from
255:           query.end = to
256:           query.process
257:           # The Query.to_num of an effort always returns the value in days.
258:           workDays = query.to_num
259:           workPercent = (workDays / (weeksToReport * weeklyWorkingDays)) *
260:                         100.0
261: 
262:           remaining = endDate = nil
263:           if task['effort', scenarioIdx] > 0
264:             # The task is an effort based task.
265:             # Get the remaining effort for this task.
266:             query.start = to
267:             query.end = task['end', scenarioIdx]
268:             query.loadUnit = :days
269:             query.process
270:             remaining = query.to_s
271:           else
272:             # The task is a duration task.
273:             # Get the planned task end date.
274:             endDate = task['end', scenarioIdx]
275:           end
276: 
277:           # Put all data into a TSTaskRecord and push it into the resource
278:           # record.
279:           resourceRecord.tasks <<
280:             TSTaskRecord.new(task, workDays, workPercent, remaining, endDate)
281:         end
282:       end
283: 
284:       records
285:     end
indentBlock(text, indent) click to toggle source

This utility function is used to indent multi-line attributes. All attributes should be filtered through this function. Attributes that contain line breaks will be indented properly. In addition to the indentation specified by indent all but the first line will be indented after the first word of the first line. The text may not end with a line break.

     # File lib/taskjuggler/reports/TimeSheetReport.rb, line 293
293:     def indentBlock(text, indent)
294:       out = ''
295:       firstSpace = 0
296:       text.length.times do |i|
297:         if firstSpace == 0 && text[i] == \ \# There must be a space after ?
298:           firstSpace = i
299:         end
300:         out << text[i]
301:         if text[i] == \n\
302:           out += ' ' * (indent + firstSpace)
303:         end
304:       end
305:       out
306:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.