lib/reports/TableReport.rb in taskjuggler-0.0.4 vs lib/reports/TableReport.rb in taskjuggler-0.0.5

- old
+ new

@@ -36,30 +36,34 @@ 'duration' => [ 'Duration', true, :right, true ], 'effort' => [ 'Effort', true, :right, true ], 'effortdone' => [ 'Effort Done', true, :right, true ], 'effortleft' => [ 'Effort Left', true, :right, true ], 'freetime' => [ 'Free Time', true, :right, true ], + 'followers' => [ 'Followers', false, :left, true ], 'id' => [ 'Id', false, :left, false ], + 'journal' => [ 'Journal', false, :left, false ], 'line' => [ 'Line No.', false, :right, false ], 'name' => [ 'Name', true, :left, false ], 'no' => [ 'No.', false, :right, false ], + 'precursors' => [ 'Precursors', false, :left, true ], 'rate' => [ 'Rate', true, :right, true ], 'resources' => [ 'Resources', false, :left, true ], 'responsible' => [ 'Responsible', false, :left, true ], 'revenue' => [ 'Revenue', true, :right, true ], 'scenario' => [ 'Scenario', false, :left, true ], 'status' => [ 'Status', false, :left, true ], 'targets' => [ 'Targets', false, :left, true ], 'wbs' => [ 'WBS', false, :left, false ] } @@propertiesByType = { - # Type Indent Align - DateAttribute => [ false, :left ], - FixnumAttribute => [ false, :right ], - FloatAttribute => [ false, :right ], - RichTextAttribute => [ false, :left ], - StringAttribute => [ false, :left ] + # Type Indent Align + DateAttribute => [ false, :left ], + FixnumAttribute => [ false, :right ], + FloatAttribute => [ false, :right ], + ResourceListAttribute => [ false, :left ], + RichTextAttribute => [ false, :left ], + StringAttribute => [ false, :left ] } # Generate a new TableReport object. def initialize(report) super @report.content = self @@ -74,61 +78,41 @@ def generateIntermediateFormat super end - - # Turn the TableReport into an equivalent HTML element tree. def to_html html = [] html << rt_to_html('header') - html << (table = XMLElement.new('table', 'summary' => 'Report Table', - 'cellspacing' => '2', 'border' => '0', - 'cellpadding' => '0', 'align' => 'center', - 'class' => 'tjtable')) + html << (tableFrame = generateHtmlTableFrame) - # The headline is put in a sub-table to appear bigger. - if a('headline') - table << (thead = XMLElement.new('thead')) - thead << (tr = XMLElement.new('tr')) - tr << (td = XMLElement.new('td')) - td << (table1 = XMLElement.new('table', 'summary' => 'headline', - 'cellspacing' => '1', 'border' => '0', - 'cellpadding' => '5', - 'align' => 'center', 'width' => '100%')) - table1 << (tr1 = XMLElement.new('tr')) - tr1 << (td1 = XMLElement.new('td', 'align' => 'center', - 'style' => 'font-size:16px; ' + - 'font-weight:bold; ' + - 'padding:5px', - 'class' => 'tabfront')) - td1 << a('headline').to_html + # Now generate the actual table with the data. + tableFrame << generateHtmlTableRow do + td = XMLElement.new('td') + td << @table.to_html + td end - # Now generate the actual table with the data. - table << (tbody = XMLElement.new('tbody')) - tbody << (tr = XMLElement.new('tr')) - tr << (td = XMLElement.new('td')) - td << @table.to_html - # Embedd the caption as RichText into the table footer. if a('caption') - tbody << (tr = XMLElement.new('tr')) - tr << (td = XMLElement.new('td', 'class' => 'tabback')) - td << (div = XMLElement.new('div', 'class' => 'caption', - 'style' => 'margin:1px')) - a('caption').sectionNumbers = false - div << a('caption').to_html + tableFrame << generateHtmlTableRow do + td = XMLElement.new('td') + td << (div = XMLElement.new('div', 'class' => 'tj_table_caption')) + a('caption').sectionNumbers = false + div << a('caption').to_html + td + end end - # A sub-table with the legend. - tbody << (tr = XMLElement.new('tr', 'style' => 'font-size:10px;')) - tr << (td = XMLElement.new('td', 'style' => - 'padding-left:1px; padding-right:1px;')) - td << @legend.to_html + # The legend. + tableFrame << generateHtmlTableRow do + td = XMLElement.new('td') + td << @legend.to_html + td + end html << rt_to_html('footer') html end @@ -551,14 +535,12 @@ cell.hidden = true cell.text = nil # The GanttChart can be reached via the special variable of the column # header. chart = columnDef.column.cell1.special - GanttLine.new(chart, property, line.scopeProperty, - query.scenarioIdx, - (line.subLineNo - 1) * (line.height + 1), - line.height) + GanttLine.new(chart, query, (line.subLineNo - 1) * (line.height + 1), + line.height, a('selfcontained') ? nil : columnDef.tooltip) return true # The calendar cells can be all generated by the same function. But we # need to use different parameters. when 'hourly' start = @start.midnight @@ -702,10 +684,12 @@ 'resource' end else nil end + cell.iconTooltip = RichText.new("'''ID:''' #{property.fullId}"). + generateIntermediateFormat when 'no' cell.text = line.no.to_s when 'wbs' cell.indent = 2 if line.scopeLine when 'scenario' @@ -740,54 +724,67 @@ # object. Therefor a single @table column usually has many cells on each # single line. _scenarioIdx_ is the index of the scenario that is reported # in this line. _line_ is the @table line. _t_ is the start date for the # calendar. _sameTimeNextFunc_ is the function that will move the date to # the next cell. - def genCalChartTaskCell(query, line, columnDef, t, sameTimeNextFunc) + def genCalChartTaskCell(origQuery, line, columnDef, t, sameTimeNextFunc) task = line.property # Find out if we have an enclosing resource scope. if line.scopeLine && line.scopeLine.property.is_a?(Resource) resource = line.scopeLine.property else resource = nil end # Get the interval of the task. In case a date is invalid due to a # scheduling problem, we use the full project interval. - taskIv = Interval.new(task['start', query.scenarioIdx].nil? ? - @project['start'] : task['start', query.scenarioIdx], - task['end', query.scenarioIdx].nil? ? - @project['end'] : task['end', query.scenarioIdx]) + taskStart = task['start', origQuery.scenarioIdx] + taskEnd = task['end', origQuery.scenarioIdx] + taskIv = Interval.new(taskStart.nil? ? @project['start'] : taskStart, + taskEnd.nil? ? @project['end'] : taskEnd) firstCell = nil while t < @end - # Create a new cell - cell = newCell(query, line) - + # We modify the start and end dates to match the cell boundaries. So + # we need to make sure we don't modify the original Query but our own + # copies. + query = origQuery.dup # call TjTime::sameTimeNext... function nextT = t.send(sameTimeNextFunc) cellIv = Interval.new(t, nextT) case columnDef.content when 'empty' + # Create a new cell + cell = newCell(query, line) # We only generate cells will different background colors. when 'load' query.attributeId = 'effort' - query.startIdx = t - query.endIdx = nextT + query.start = t + query.end = nextT query.process + + # Create a new cell + cell = newCell(query, line) + # To increase readability, we don't show 0.0 values. cell.text = query.to_s if query.to_num != 0.0 else raise "Unknown column content #{column.content}" end # Determine cell category (mostly the background color) if cellIv.overlaps?(taskIv) + # The cell is either a container or leaf task cell.category = task.container? ? 'calconttask' : 'caltask' + # If the user has requested a custom tooltip, add it to each task cell. + cell.tooltip = columnDef.tooltip.getPattern(query) || nil + cell.showTooltipHint = false elsif !@project.isWorkingTime(cellIv) + # The cell is a vacation cell. cell.category = 'offduty' else + # The cell is just filled with the background color. cell.category = 'taskcell' end cell.category += line.property.get('index') % 2 == 1 ? '1' : '2' tryCellMerging(cell, line, firstCell) @@ -806,29 +803,33 @@ # ColumnTable object. Therefor a single @table column usually has many cells # on each single line. _scenarioIdx_ is the index of the scenario that is # reported in this line. _line_ is the @table line. _t_ is the start date # for the calendar. _sameTimeNextFunc_ is the function that will move the # date to the next cell. - def genCalChartResourceCell(query, line, columnDef, t, + def genCalChartResourceCell(origQuery, line, columnDef, t, sameTimeNextFunc) resource = line.property # Find out if we have an enclosing task scope. if line.scopeLine && line.scopeLine.property.is_a?(Task) task = line.scopeLine.property # Get the interval of the task. In case a date is invalid due to a # scheduling problem, we use the full project interval. - taskIv = Interval.new(task['start', query.scenarioIdx].nil? ? - @project['start'] : - task['start', query.scenarioIdx], - task['end', query.scenarioIdx].nil? ? - @project['end'] : task['end', query.scenarioIdx]) + taskStart = task['start', origQuery.scenarioIdx] + taskEnd = task['end', origQuery.scenarioIdx] + taskIv = Interval.new(taskStart.nil? ? @project['start'] : taskStart, + taskEnd.nil? ? @project['end'] : taskEnd) else task = nil end firstCell = nil while t < @end + # We modify the start and end dates to match the cell boundaries. So + # we need to make sure we don't modify the original Query but our own + # copies. + query = origQuery.dup + # Create a new cell cell = newCell(query, line) # call TjTime::sameTimeNext... function nextT = t.send(sameTimeNextFunc) @@ -866,26 +867,33 @@ end else raise "Unknown column content #{column.content}" end + # Set the tooltip for the cell. We might delete it again. + cell.tooltip = columnDef.tooltip.getPattern(query) || nil + cell.showTooltipHint = false + # Determine cell category (mostly the background color) cell.category = if task if cellIv.overlaps?(taskIv) if workLoadTask > 0.0 && freeLoad == 0.0 'busy' elsif workLoad == 0.0 && freeLoad == 0.0 + cell.tooltip = nil 'offduty' else 'loaded' end else if freeLoad > 0.0 'free' elsif workLoad == 0.0 && freeLoad == 0.0 + cell.tooltip = nil 'offduty' else + cell.tooltip = nil 'resourcecell' end end else if workLoad > 0.0 && freeLoad == 0.0 @@ -893,9 +901,10 @@ elsif workLoad > 0.0 && freeLoad > 0.0 'loaded' elsif workLoad == 0.0 && freeLoad > 0.0 'free' else + cell.tooltip = nil 'offduty' end end cell.category += line.property.get('index') % 2 == 1 ? '1' : '2'