lib/reports/Report.rb in taskjuggler-0.0.3 vs lib/reports/Report.rb in taskjuggler-0.0.4

- old
+ new

@@ -1,22 +1,24 @@ #!/usr/bin/env ruby -w # encoding: UTF-8 # # = Report.rb -- The TaskJuggler III Project Management Software # -# Copyright (c) 2006, 2007, 2008, 2009 by Chris Schlaeger <cs@kde.org> +# Copyright (c) 2006, 2007, 2008, 2009, 2010 by Chris Schlaeger <cs@kde.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # +require 'fileutils' require 'PropertyTreeNode' require 'reports/TextReport' require 'reports/TaskListRE' require 'reports/ResourceListRE' require 'reports/TjpExportRE' +require 'reports/TimeSheetReport' require 'reports/CSVFile' require 'reports/Navigator' require 'reports/ReportContext' require 'HTMLDocument' @@ -44,18 +46,20 @@ # generated according the the requested output format(s). def generate begin @content = nil case @typeSpec - when :export - # Does not have an intermediate representation. Nothing to do here. when :resourcereport @content = ResourceListRE.new(self) when :textreport @content = TextReport.new(self) when :taskreport @content = TaskListRE.new(self) + when :export + @content = TjpExportRE.new(self) + when :timeSheet + @content = TimeSheetReport.new(self) else raise "Unknown report type" end # Most output format can be generated from a common intermediate @@ -65,23 +69,23 @@ # Then generate the actual output format. get('formats').each do |format| case format when :html generateHTML + copyAuxiliaryFiles when :csv generateCSV - when :export - generateExport + when :tjp + generateTJP else raise 'Unknown report output format.' end end rescue TjException - @project.messageHandler.send(Message.new('reporting_failed', 'error', - $!.message, nil, nil, - @sourceFileInfo)) + error('reporting_failed', $!.message) end + 0 end # Render the content of the report as HTML (without the framing). def to_html @content ? @content.to_html : nil @@ -97,180 +101,72 @@ # Generate an HTML version of the report. def generateHTML html = HTMLDocument.new(:transitional) html << (head = XMLElement.new('head')) head << XMLNamedText.new("TaskJuggler Report - #{@name}", 'title') - head << XMLElement.new('meta', 'http-equiv' => 'Content-Style-Type', - 'content' => 'text/css; charset=utf-8') - head << (style = XMLElement.new('style', 'type' => 'text/css')) - style << XMLBlob.new(<<'EOT' - body { - font-family:Bitstream Vera Sans, Tahoma, sans-serif; - font-size:15px; - } - h1, h2, table, tr, td, div, span { - font-family: Bitstream Vera Sans, Tahoma, sans-serif; - } - table { - font-size:13px; - } - td, div { white-space:nowrap; padding:0px; margin:0px; } - h1 { font-size:22px; } - h2 { font-size:18px; } - h3 { font-size:16px; } - - .tabback { background-color:#9a9a9a; } - .tabfront { background-color:#d4dde6; } - .tabhead { - white-space:nowrap; - background-color:#7a7a7a; - color:#ffffff; - text-align:center; - } - .tabhead_offduty { - white-space:nowrap; - background-color:#dde375; - color:#000000; - } - .tabfooter { - white-space:nowrap; - background-color:#9a9a9a; - color:#ffffff; - text-align:center; - } - .headercelldiv { - padding-top:1px; - padding-right:3px; - padding-left:3px; - padding-bottom:0px; - white-space:nowrap; - overflow:auto; - } - .celldiv { - padding-top:3px; - padding-right:3px; - padding-left:3px; - padding-bottom:0px; - white-space:nowrap; - overflow:auto; - } - .tabline { color:#000000 } - .tabcell { - white-space:nowrap; - overflow:hidden; - padding:0px; - } - .taskcell1 { - background-color:#ebf2ff; - white-space:nowrap; - padding:0px; - } - .taskcell2 { - background-color:#d9dfeb; - white-space:nowrap; - padding:0px; - } - .resourcecell1 { - background-color:#fff2eb; - white-space:nowrap; - padding:0px; - } - .resourcecell2 { - background-color:#ebdfd9; - white-space:nowrap; - padding:0px; - } - .busy1 { background-color:#ff3b3b; } - .busy2 { background-color:#eb4545; } - .loaded1 { background-color:#ff9b9b; } - .loaded2 { background-color:#eb8f8f; } - .free1 { background-color:#a5ffb4; } - .free2 { background-color:#98eba6; } - .offduty1 { background-color:#f3f990; } - .offduty2 { background-color:#dde375; } - .calconttask1 { background-color:#abbeae; } - .calconttask2 { background-color:#99aa9c; } - .caltask1 { background-color:#2050e5; } - .caltask2 { background-color:#2f57ea; } - .todo1 { background-color:#beabab; } - .todo2 { background-color:#aa9999; } - - .tabvline { - background-color:#9a9a9a; - position:absolute; - } - .containerbar { - background-color:#09090a; - position:absolute; - } - .taskbarframe { - background-color:#09090a; - position:absolute; - } - .taskbar { - background-color:#2f57ea; - position:absolute; - } - .progressbar { - background-color:#36363f; - position:absolute; - } - .milestone { - background-color:#09090a; - position:absolute; - } - .loadstackframe { - background-color:#452a2a; - position:absolute; - } - .free { - background-color:#a5ffb5; - position:absolute; - } - .busy { - background-color:#ff9b9b; - position:absolute; - } - .assigned { - background-color:#ff3b3b; - position:absolute; - } - .offduty { - background-color:#f3f990; - white-space:nowrap; - position:absolute; - } - .depline { - background-color:#000000; - position:absolute; - } - .nowline { - background-color:#EE0000; - position:absolute; - } - .white { - background-color:#FFFFFF; - position:absolute; - } - - .legendback { background-color:#d4dde6; } - .caption { - padding: 5px 13px 5px 13px; - background-color:#ebf2ff; - white-space:normal; - font-size:13px - } + if a('selfcontained') + auxSrcDir = AppConfig.dataDirs('data/css')[0] + cssFileName = auxSrcDir + '/tjreport.css' + if auxSrcDir.nil? || !File.exists?(cssFileName) + raise TjException.new, <<"EOT" +Cannot find '#{cssFileName}'. This is usually the result of an improper +TaskJuggler installation. If you know where to find the data directory, you +can use the TASKJUGGLER_DATA_PATH environment variable to specify the +location. EOT - ) + end + cssFile = IO.read(cssFileName) + if cssFile.empty? + raise TjException.new, <<"EOT" +Cannot read '#{cssFileName}'. Make sure the file is not empty and you have +read access permission. +EOT + end + head << XMLElement.new('meta', 'http-equiv' => 'Content-Style-Type', + 'content' => 'text/css; charset=utf-8') + head << (style = XMLElement.new('style', 'type' => 'text/css')) + style << XMLBlob.new("\n" + cssFile) + else + head << XMLElement.new('link', 'rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => 'css/tjreport.css') + end html << (body = XMLElement.new('body')) + unless a('selfcontained') + body << (script = XMLElement.new('script', 'type' => 'text/javascript', + 'src' => 'scripts/wz_tooltip.js')) + script.mayNotBeEmpty = true + body << (noscript = XMLElement.new('noscript')) + noscript << (nsdiv = XMLElement.new('div', + 'style' => 'text-align:center; ' + + 'color:#FF0000')) + nsdiv << XMLText.new(<<'EOT' +This page requires Javascript for full functionality. Please enable it +in your browser settings! +EOT + ) + end + + # Make sure we have some margins around the report. body << (frame = XMLElement.new('div', 'style' => 'margin: 35px 5% 25px 5%; ')) frame << @content.to_html if @content + # The footer with some administrative information. + frame << (div = XMLElement.new('div', 'class' => 'copyright')) + div << XMLText.new(@project['copyright'] + " - ") if @project['copyright'] + div << XMLText.new("Project: #{@project['name']} " + + "Version: #{@project['version']} - " + + "Created on #{TjTime.now.to_s("%Y-%m-%d %H:%M:%S")} " + + "with ") + div << XMLNamedText.new("#{AppConfig.softwareName}", 'a', + 'href' => "#{AppConfig.contact}") + div << XMLText.new(" v#{AppConfig.version}") + html.write((@name[0] == '/' ? '' : @project.outputDir) + @name + (@name == '.' ? '' : '.html')) end # Generate a CSV version of the report. @@ -312,16 +208,63 @@ # separated file. Write to $stdout if the filename was set to '.'. CSVFile.new(csv, ';').write((@name[0] == '/' ? '' : @project.outputDir) + @name + (@name == '.' ? '' : '.csv')) end - # Generate an export report - def generateExport - @content = TjpExportRE.new(self) + # Generate time sheet drafts. + def generateTJP f = @name == '.' ? $stdout : File.new((@name[0] == '/' ? '' : @project.outputDir) + @name, 'w') f.puts "#{@content.to_tjp}" + end + + def copyAuxiliaryFiles + return if @name == '.' # Don't copy files if output is stdout. + + copyDirectory('css') + copyDirectory('icons') + copyDirectory('scripts') + end + + def copyDirectory(dirName) + # The directory needs to be in the same directory as the HTML report. + auxDstDir = File.dirname((@name[0] == '/' ? '' : @project.outputDir) + + @name) + '/' + # Find the data directory that came with the TaskJuggler installation. + auxSrcDir = AppConfig.dataDirs("data/#{dirName}")[0] + if auxSrcDir.nil? || !File.exists?(auxSrcDir) + raise TjException.new, <<"EOT" +Cannot find the #{dirName} directory. This is usually +the result of an improper TaskJuggler installation. If you know the directory, +you can use the TASKJUGGLER_DATA_PATH environment variable to specify the +location. +EOT + end + # Don't copy directory if all files are up-to-date. + return if directoryUpToDate?(auxSrcDir, auxDstDir + dirName) + + # Recursively copy the directory and all content. + FileUtils.cp_r(auxSrcDir, auxDstDir) + end + + def directoryUpToDate?(auxSrcDir, auxDstDir) + return false unless File.exists?(auxDstDir.untaint) + + Dir.entries(auxSrcDir).each do |file| + next if file == '.' || file == '..' + + srcFile = (auxSrcDir + '/' + file).untaint + dstFile = (auxDstDir + '/' + file).untaint + return false if !File.exist?(dstFile) || + File.mtime(srcFile) > File.mtime(dstFile) + end + true + end + + def error(id, message) + @project.messageHandler.send(Message.new(id, 'error', message, nil, nil, + @sourceFileInfo)) end end end