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