taskjuggler 3.1.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +44 -0
- data/bin/tj3webd +4 -0
- data/data/css/tjreport.css +14 -5
- data/data/tjp.vim +22 -7
- data/examples/Fedora-20/reports.tji +2 -4
- data/examples/Scrum/Product Burndown.csv +26 -0
- data/examples/Scrum/Sprint 1 Burndown.csv +26 -0
- data/examples/Scrum/Sprint 2 Burndown.csv +26 -0
- data/examples/Scrum/Sprint 3 Burndown.csv +26 -0
- data/examples/Scrum/scrum.tjp +141 -0
- data/examples/Tutorial/tutorial.tjp +13 -7
- data/lib/taskjuggler/Attributes.rb +2 -3
- data/lib/taskjuggler/HTMLDocument.rb +25 -18
- data/lib/taskjuggler/Journal.rb +85 -65
- data/lib/taskjuggler/KeywordDocumentation.rb +25 -13
- data/lib/taskjuggler/LeaveList.rb +1 -1
- data/lib/taskjuggler/Limits.rb +3 -5
- data/lib/taskjuggler/MessageHandler.rb +173 -19
- data/lib/taskjuggler/Painter.rb +75 -0
- data/lib/taskjuggler/Painter/BasicShapes.rb +76 -0
- data/lib/taskjuggler/Painter/Color.rb +273 -0
- data/lib/taskjuggler/Painter/Element.rb +56 -0
- data/lib/taskjuggler/Painter/FontData.rb +221 -0
- data/lib/taskjuggler/Painter/FontMetrics.rb +125 -0
- data/lib/taskjuggler/Painter/FontMetricsData.rb +151 -0
- data/lib/taskjuggler/Painter/Group.rb +77 -0
- data/lib/taskjuggler/Painter/Points.rb +47 -0
- data/lib/taskjuggler/Painter/Primitives.rb +100 -0
- data/lib/taskjuggler/Painter/SVGSupport.rb +36 -0
- data/lib/taskjuggler/Painter/Text.rb +36 -0
- data/lib/taskjuggler/Project.rb +46 -29
- data/lib/taskjuggler/ProjectFileParser.rb +24 -22
- data/lib/taskjuggler/ProjectFileScanner.rb +2 -2
- data/lib/taskjuggler/PropertyTreeNode.rb +26 -34
- data/lib/taskjuggler/Query.rb +8 -5
- data/lib/taskjuggler/RealFormat.rb +3 -0
- data/lib/taskjuggler/Resource.rb +3 -5
- data/lib/taskjuggler/ResourceScenario.rb +19 -7
- data/lib/taskjuggler/RichText.rb +4 -6
- data/lib/taskjuggler/RichText/FunctionExample.rb +1 -1
- data/lib/taskjuggler/RichText/FunctionHandler.rb +5 -6
- data/lib/taskjuggler/RichText/Parser.rb +4 -6
- data/lib/taskjuggler/RichText/RTFNavigator.rb +1 -1
- data/lib/taskjuggler/RichText/RTFQuery.rb +2 -3
- data/lib/taskjuggler/RichText/RTFReport.rb +1 -1
- data/lib/taskjuggler/RichText/RTFReportLink.rb +1 -2
- data/lib/taskjuggler/RichText/RTFWithQuerySupport.rb +1 -1
- data/lib/taskjuggler/RichText/Scanner.rb +6 -6
- data/lib/taskjuggler/RichText/Snip.rb +1 -2
- data/lib/taskjuggler/RuntimeConfig.rb +9 -6
- data/lib/taskjuggler/ScenarioData.rb +4 -3
- data/lib/taskjuggler/Scoreboard.rb +6 -0
- data/lib/taskjuggler/SheetHandlerBase.rb +25 -8
- data/lib/taskjuggler/SimpleQueryExpander.rb +14 -5
- data/lib/taskjuggler/SyntaxReference.rb +1 -2
- data/lib/taskjuggler/TableColumnSorter.rb +84 -0
- data/lib/taskjuggler/Task.rb +3 -5
- data/lib/taskjuggler/TaskJuggler.rb +36 -29
- data/lib/taskjuggler/TaskScenario.rb +154 -66
- data/lib/taskjuggler/TextParser.rb +24 -17
- data/lib/taskjuggler/TextParser/Scanner.rb +16 -11
- data/lib/taskjuggler/TextParser/SourceFileInfo.rb +20 -15
- data/lib/taskjuggler/TimeSheets.rb +6 -12
- data/lib/taskjuggler/Tj3AppBase.rb +35 -16
- data/lib/taskjuggler/Tj3Config.rb +1 -1
- data/lib/taskjuggler/TjpSyntaxRules.rb +239 -49
- data/lib/taskjuggler/XMLElement.rb +9 -2
- data/lib/taskjuggler/apps/Tj3.rb +43 -37
- data/lib/taskjuggler/apps/Tj3Client.rb +62 -112
- data/lib/taskjuggler/apps/Tj3Daemon.rb +66 -29
- data/lib/taskjuggler/apps/Tj3Man.rb +5 -5
- data/lib/taskjuggler/apps/Tj3SsReceiver.rb +10 -13
- data/lib/taskjuggler/apps/Tj3SsSender.rb +13 -16
- data/lib/taskjuggler/apps/Tj3TsReceiver.rb +10 -13
- data/lib/taskjuggler/apps/Tj3TsSender.rb +12 -15
- data/lib/taskjuggler/apps/Tj3TsSummary.rb +12 -15
- data/lib/taskjuggler/apps/Tj3WebD.rb +99 -0
- data/lib/taskjuggler/daemon/Daemon.rb +50 -10
- data/lib/taskjuggler/daemon/DaemonConnector.rb +127 -0
- data/lib/taskjuggler/daemon/ProcessIntercom.rb +36 -21
- data/lib/taskjuggler/daemon/ProjectBroker.rb +122 -112
- data/lib/taskjuggler/daemon/ProjectServer.rb +78 -46
- data/lib/taskjuggler/daemon/ReportServer.rb +52 -28
- data/lib/taskjuggler/daemon/ReportServlet.rb +92 -21
- data/lib/taskjuggler/daemon/WebServer.rb +75 -22
- data/lib/taskjuggler/daemon/WelcomePage.rb +1 -0
- data/lib/taskjuggler/reports/AccountListRE.rb +3 -3
- data/lib/taskjuggler/reports/CSVFile.rb +9 -2
- data/lib/taskjuggler/reports/ChartPlotter.rb +453 -0
- data/lib/taskjuggler/reports/Navigator.rb +1 -0
- data/lib/taskjuggler/reports/NikuReport.rb +4 -4
- data/lib/taskjuggler/reports/Report.rb +6 -18
- data/lib/taskjuggler/reports/ReportBase.rb +9 -9
- data/lib/taskjuggler/reports/ReportContext.rb +2 -2
- data/lib/taskjuggler/reports/StatusSheetReport.rb +6 -6
- data/lib/taskjuggler/reports/TableReport.rb +24 -15
- data/lib/taskjuggler/reports/TimeSheetReport.rb +5 -5
- data/lib/taskjuggler/reports/TraceReport.rb +251 -0
- data/lib/tj3webd.rb +17 -0
- data/manual/Day_To_Day_Juggling +10 -3
- data/manual/Installation +38 -19
- data/manual/Software +25 -19
- data/manual/Tutorial +119 -110
- data/manual/html/Day_To_Day_Juggling.html +7 -5
- data/manual/html/Getting_Started.html +4 -4
- data/manual/html/How_To_Contribute.html +4 -4
- data/manual/html/Installation.html +19 -11
- data/manual/html/Intro.html +4 -4
- data/manual/html/Reporting_Bugs.html +4 -4
- data/manual/html/Rich_Text_Attributes.html +4 -4
- data/manual/html/Software.html +15 -11
- data/manual/html/TaskJuggler_2x_Migration.html +4 -4
- data/manual/html/TaskJuggler_Internals.html +4 -4
- data/manual/html/The_TaskJuggler_Syntax.html +4 -4
- data/manual/html/Tutorial.html +41 -32
- data/manual/html/account.html +4 -4
- data/manual/html/account.task.html +4 -4
- data/manual/html/accountprefix.html +4 -4
- data/manual/html/accountreport.html +27 -9
- data/manual/html/accountroot.html +5 -5
- data/manual/html/active.html +4 -4
- data/manual/html/adopt.task.html +4 -4
- data/manual/html/aggregate.html +4 -4
- data/manual/html/alert.html +4 -4
- data/manual/html/alertlevels.html +4 -4
- data/manual/html/allocate.html +5 -5
- data/manual/html/alphabet.html +4 -4
- data/manual/html/alternative.html +4 -4
- data/manual/html/author.html +4 -4
- data/manual/html/balance.html +5 -5
- data/manual/html/booking.resource.html +4 -4
- data/manual/html/booking.task.html +4 -4
- data/manual/html/caption.html +5 -5
- data/manual/html/cellcolor.column.html +43 -8
- data/manual/html/celltext.column.html +4 -4
- data/manual/html/center.html +5 -5
- data/manual/html/charge.html +4 -4
- data/manual/html/chargeset.html +4 -4
- data/manual/html/columnid.html +27 -15
- data/manual/html/columns.html +5 -5
- data/manual/html/complete.html +4 -4
- data/manual/html/copyright.html +4 -4
- data/manual/html/credits.html +4 -4
- data/manual/html/css/tjreport.css +14 -5
- data/manual/html/currency.html +4 -4
- data/manual/html/currencyformat.html +5 -5
- data/manual/html/dailymax.html +5 -5
- data/manual/html/dailymin.html +5 -5
- data/manual/html/dailyworkinghours.html +4 -4
- data/manual/html/date.extend.html +4 -4
- data/manual/html/date.html +5 -5
- data/manual/html/definitions.html +4 -4
- data/manual/html/depends.html +4 -4
- data/manual/html/details.html +4 -4
- data/manual/html/disabled.html +4 -4
- data/manual/html/duration.html +4 -4
- data/manual/html/efficiency.html +4 -4
- data/manual/html/effort.html +4 -4
- data/manual/html/email.html +4 -4
- data/manual/html/enabled.html +4 -4
- data/manual/html/end.column.html +4 -4
- data/manual/html/end.html +4 -4
- data/manual/html/end.limit.html +4 -4
- data/manual/html/end.report.html +5 -5
- data/manual/html/end.timesheet.html +4 -4
- data/manual/html/endcredit.html +4 -4
- data/manual/html/epilog.html +5 -5
- data/manual/html/export.html +4 -4
- data/manual/html/extend.html +4 -4
- data/manual/html/fail.html +43 -8
- data/manual/html/fdl.html +4 -4
- data/manual/html/flags.account.html +4 -4
- data/manual/html/flags.html +4 -4
- data/manual/html/flags.journalentry.html +4 -4
- data/manual/html/flags.report.html +5 -5
- data/manual/html/flags.resource.html +4 -4
- data/manual/html/flags.statussheet.html +4 -4
- data/manual/html/flags.task.html +4 -4
- data/manual/html/flags.timesheet.html +4 -4
- data/manual/html/fontcolor.column.html +43 -8
- data/manual/html/footer.html +5 -5
- data/manual/html/formats.html +5 -5
- data/manual/html/functions.html +4 -4
- data/manual/html/gapduration.html +4 -4
- data/manual/html/gaplength.html +4 -4
- data/manual/html/halign.center.html +4 -4
- data/manual/html/halign.column.html +43 -8
- data/manual/html/halign.left.html +4 -4
- data/manual/html/halign.right.html +4 -4
- data/manual/html/hasalert.html +4 -4
- data/manual/html/header.html +5 -5
- data/manual/html/headline.html +7 -7
- data/manual/html/height.html +72 -0
- data/manual/html/hideaccount.html +46 -11
- data/manual/html/hidejournalentry.html +5 -5
- data/manual/html/hidereport.html +43 -8
- data/manual/html/hideresource.html +44 -9
- data/manual/html/hidetask.html +44 -9
- data/manual/html/icalreport.html +4 -4
- data/manual/html/include.macro.html +4 -4
- data/manual/html/include.project.html +4 -4
- data/manual/html/include.properties.html +4 -4
- data/manual/html/index.html +2 -1
- data/manual/html/inherit.extend.html +4 -4
- data/manual/html/interval1.html +4 -4
- data/manual/html/interval2.html +4 -4
- data/manual/html/interval3.html +4 -4
- data/manual/html/interval4.html +4 -4
- data/manual/html/isactive.html +4 -4
- data/manual/html/ischildof.html +4 -4
- data/manual/html/isdependencyof.html +4 -4
- data/manual/html/isdutyof.html +4 -4
- data/manual/html/isfeatureof.html +4 -4
- data/manual/html/isleaf.html +4 -4
- data/manual/html/ismilestone.html +4 -4
- data/manual/html/isongoing.html +4 -4
- data/manual/html/isresource.html +4 -4
- data/manual/html/isresponsibilityof.html +4 -4
- data/manual/html/istask.html +4 -4
- data/manual/html/journalattributes.html +11 -7
- data/manual/html/journalentry.html +4 -4
- data/manual/html/journalmode.html +5 -5
- data/manual/html/leaveallowance.html +5 -5
- data/manual/html/leaves.html +5 -6
- data/manual/html/left.html +5 -5
- data/manual/html/length.html +4 -4
- data/manual/html/limits.allocate.html +4 -4
- data/manual/html/limits.html +4 -4
- data/manual/html/limits.resource.html +4 -4
- data/manual/html/limits.task.html +4 -4
- data/manual/html/listitem.column.html +4 -4
- data/manual/html/listtype.column.html +4 -4
- data/manual/html/loadunit.html +5 -5
- data/manual/html/logicalexpression.html +8 -44
- data/manual/html/logicalflagexpression.html +4 -4
- data/manual/html/macro.html +4 -4
- data/manual/html/managers.html +4 -4
- data/manual/html/mandatory.html +4 -4
- data/manual/html/maxend.html +4 -4
- data/manual/html/maximum.html +5 -5
- data/manual/html/maxstart.html +4 -4
- data/manual/html/milestone.html +4 -4
- data/manual/html/minend.html +4 -4
- data/manual/html/minimum.html +5 -5
- data/manual/html/minstart.html +4 -4
- data/manual/html/monthlymax.html +5 -5
- data/manual/html/monthlymin.html +5 -5
- data/manual/html/navbar.html +10 -4
- data/manual/html/navigator.html +4 -4
- data/manual/html/newtask.html +4 -4
- data/manual/html/nikureport.html +4 -4
- data/manual/html/note.task.html +4 -4
- data/manual/html/now.html +4 -4
- data/manual/html/numberformat.html +5 -5
- data/manual/html/onend.html +4 -4
- data/manual/html/onstart.html +4 -4
- data/manual/html/opennodes.html +5 -5
- data/manual/html/overtime.booking.html +4 -4
- data/manual/html/period.column.html +4 -4
- data/manual/html/period.limit.html +4 -4
- data/manual/html/period.report.html +5 -5
- data/manual/html/period.task.html +4 -4
- data/manual/html/persistent.html +4 -4
- data/manual/html/precedes.html +4 -4
- data/manual/html/priority.html +4 -4
- data/manual/html/priority.timesheet.html +4 -4
- data/manual/html/project.html +4 -4
- data/manual/html/projectid.html +4 -4
- data/manual/html/projectid.task.html +4 -4
- data/manual/html/projectids.html +4 -4
- data/manual/html/projection.html +5 -7
- data/manual/html/prolog.html +5 -5
- data/manual/html/properties.html +11 -5
- data/manual/html/purge.html +5 -5
- data/manual/html/rate.html +4 -4
- data/manual/html/rate.resource.html +4 -4
- data/manual/html/reference.extend.html +4 -4
- data/manual/html/remaining.html +4 -4
- data/manual/html/replace.html +4 -4
- data/manual/html/reportprefix.html +4 -4
- data/manual/html/resource.html +4 -10
- data/manual/html/resourceattributes.html +4 -4
- data/manual/html/resourceprefix.html +4 -4
- data/manual/html/resourcereport.html +28 -10
- data/manual/html/resourceroot.html +5 -5
- data/manual/html/resources.limit.html +4 -4
- data/manual/html/responsible.html +4 -4
- data/manual/html/richtext.extend.html +4 -4
- data/manual/html/right.html +5 -5
- data/manual/html/rollupaccount.html +44 -9
- data/manual/html/rollupresource.html +44 -9
- data/manual/html/rolluptask.html +44 -9
- data/manual/html/scale.column.html +4 -4
- data/manual/html/scenario.html +4 -22
- data/manual/html/scenario.ical.html +4 -4
- data/manual/html/scenarios.export.html +4 -4
- data/manual/html/scenarios.html +5 -5
- data/manual/html/scenariospecific.extend.html +4 -4
- data/manual/html/scheduled.html +4 -4
- data/manual/html/scheduling.html +4 -4
- data/manual/html/select.html +4 -4
- data/manual/html/selfcontained.html +5 -5
- data/manual/html/shift.allocate.html +4 -4
- data/manual/html/shift.html +4 -4
- data/manual/html/shift.resource.html +5 -5
- data/manual/html/shift.task.html +4 -4
- data/manual/html/shift.timesheet.html +4 -4
- data/manual/html/shifts.allocate.html +4 -4
- data/manual/html/shifts.resource.html +4 -4
- data/manual/html/shifts.task.html +4 -4
- data/manual/html/shorttimeformat.html +4 -4
- data/manual/html/sloppy.booking.html +4 -4
- data/manual/html/sloppy.projection.html +5 -5
- data/manual/html/sortaccounts.html +5 -5
- data/manual/html/sortjournalentries.html +5 -5
- data/manual/html/sortresources.html +5 -5
- data/manual/html/sorttasks.html +5 -5
- data/manual/html/start.column.html +4 -4
- data/manual/html/start.html +4 -4
- data/manual/html/start.limit.html +4 -4
- data/manual/html/start.report.html +5 -5
- data/manual/html/startcredit.html +4 -4
- data/manual/html/status.statussheet.html +4 -4
- data/manual/html/status.timesheet.html +4 -4
- data/manual/html/statussheet.html +4 -4
- data/manual/html/statussheetreport.html +4 -4
- data/manual/html/strict.projection.html +5 -5
- data/manual/html/summary.html +4 -4
- data/manual/html/supplement.html +4 -4
- data/manual/html/supplement.resource.html +4 -10
- data/manual/html/supplement.task.html +4 -28
- data/manual/html/tagfile.html +4 -4
- data/manual/html/task.html +4 -28
- data/manual/html/task.statussheet.html +4 -4
- data/manual/html/task.timesheet.html +4 -4
- data/manual/html/taskattributes.html +4 -4
- data/manual/html/taskprefix.html +4 -4
- data/manual/html/taskreport.html +28 -10
- data/manual/html/taskroot.html +5 -5
- data/manual/html/text.extend.html +4 -4
- data/manual/html/textreport.html +27 -9
- data/manual/html/timeformat.html +5 -5
- data/manual/html/timeoff.nikureport.html +4 -4
- data/manual/html/timesheet.html +4 -4
- data/manual/html/timesheetreport.html +23 -5
- data/manual/html/timezone.export.html +4 -4
- data/manual/html/timezone.html +4 -4
- data/manual/html/timezone.report.html +5 -5
- data/manual/html/timezone.shift.html +4 -4
- data/manual/html/timingresolution.html +4 -4
- data/manual/html/title.column.html +4 -4
- data/manual/html/title.html +5 -5
- data/manual/html/toc.html +207 -179
- data/manual/html/tooltip.column.html +45 -10
- data/manual/html/tracereport.html +405 -0
- data/manual/html/trackingscenario.html +6 -6
- data/manual/html/treelevel.html +4 -4
- data/manual/html/vacation.html +4 -4
- data/manual/html/vacation.resource.html +4 -4
- data/manual/html/vacation.shift.html +4 -4
- data/manual/html/warn.html +43 -8
- data/manual/html/weeklymax.html +5 -5
- data/manual/html/weeklymin.html +5 -5
- data/manual/html/weekstartsmonday.html +4 -4
- data/manual/html/weekstartssunday.html +6 -6
- data/manual/html/width.column.html +6 -6
- data/manual/html/width.html +72 -0
- data/manual/html/work.html +4 -4
- data/manual/html/workinghours.project.html +4 -4
- data/manual/html/workinghours.resource.html +4 -4
- data/manual/html/workinghours.shift.html +4 -4
- data/manual/html/yearlyworkingdays.html +4 -4
- data/spec/Color_spec.rb +60 -0
- data/spec/ProjectBroker_spec.rb +3 -2
- data/spec/StatusSheets_spec.rb +5 -4
- data/spec/TableColumnSorter_spec.rb +78 -0
- data/spec/TimeSheets_spec.rb +6 -2
- data/spec/Tj3Daemon_spec.rb +2 -2
- data/spec/TraceReport_spec.rb +117 -0
- data/taskjuggler.gemspec +1 -1
- data/test/MessageChecker.rb +3 -1
- data/test/ReferenceGenerator.rb +1 -1
- data/test/TestSuite/CSV-Reports/Leave.tjp +1 -1
- data/test/TestSuite/CSV-Reports/refs/resourcereport_with_tasks.csv +3 -0
- data/test/TestSuite/CSV-Reports/refs/taskcounter.csv +9 -0
- data/test/TestSuite/CSV-Reports/refs/taskreport_with_resources.csv +19 -16
- data/test/TestSuite/CSV-Reports/refs/weekly.csv +1 -0
- data/test/TestSuite/Export-Reports/refs/LogicalExpression.tjp +14 -2
- data/test/TestSuite/Export-Reports/refs/tutorial.tjp +98 -86
- data/test/TestSuite/Scheduler/Correct/Leaves.tjp +25 -0
- data/test/TestSuite/Syntax/Correct/Leave.tjp +1 -1
- data/test/TestSuite/Syntax/Correct/LogicalExpression.tjp +9 -1
- data/test/TestSuite/Syntax/Correct/TraceReport.tjp +10 -0
- data/test/TestSuite/Syntax/Correct/tutorial.tjp +10 -4
- data/test/test_CSV-Reports.rb +3 -3
- data/test/test_Export-Reports.rb +91 -86
- data/test/test_Journal.rb +15 -12
- data/test/test_Limits.rb +3 -3
- data/test/test_Project.rb +1 -2
- data/test/test_ProjectFileScanner.rb +1 -1
- data/test/test_PropertySet.rb +1 -1
- data/test/test_Query.rb +5 -6
- data/test/test_ReportGenerator.rb +15 -7
- data/test/test_RichText.rb +4 -3
- data/test/test_Scheduler.rb +19 -7
- data/test/test_ShiftAssignments.rb +2 -2
- data/test/test_SimpleQueryExpander.rb +29 -2
- data/test/test_Syntax.rb +14 -5
- metadata +49 -10
- data/lib/taskjuggler/LogFile.rb +0 -73
@@ -23,12 +23,17 @@ class TaskJuggler
|
|
23
23
|
def initialize(name, attributes = {}, selfClosing = false, &block)
|
24
24
|
if (name.nil? && attributes.length > 0) ||
|
25
25
|
(!name.nil? && !name.is_a?(String))
|
26
|
-
raise "Name must be nil or a String "
|
26
|
+
raise ArgumentError, "Name must be nil or a String "
|
27
27
|
end
|
28
28
|
@name = name
|
29
29
|
attributes.each do |n, v|
|
30
30
|
if n.nil? || v.nil?
|
31
|
-
raise
|
31
|
+
raise ArgumentError,
|
32
|
+
"Attribute name (#{n}) or value (#{v}) may not be nil"
|
33
|
+
end
|
34
|
+
unless v.is_a?(String)
|
35
|
+
raise ArgumentError,
|
36
|
+
"Attribute value of #{n} must be a String"
|
32
37
|
end
|
33
38
|
end
|
34
39
|
@attributes = attributes
|
@@ -91,6 +96,8 @@ class TaskJuggler
|
|
91
96
|
|
92
97
|
# Add or change _attribute_ to _value_.
|
93
98
|
def []=(attribute, value)
|
99
|
+
raise ArgumentError,
|
100
|
+
"Attribute value #{value} is not a String" unless value.is_a?(String)
|
94
101
|
@attributes[attribute] = value
|
95
102
|
end
|
96
103
|
|
data/lib/taskjuggler/apps/Tj3.rb
CHANGED
@@ -35,6 +35,8 @@ class TaskJuggler
|
|
35
35
|
@checkSyntax = false
|
36
36
|
# Don't generate reports when previous errors have been found.
|
37
37
|
@forceReports = false
|
38
|
+
# Don't generate trace reports by default
|
39
|
+
@generateTraces = false
|
38
40
|
# Should a booking file be generated?
|
39
41
|
@freeze = false
|
40
42
|
# The cut-off date for the freeze
|
@@ -87,7 +89,8 @@ EOT
|
|
87
89
|
begin
|
88
90
|
@freezeDate = TjTime.new(arg).align(3600)
|
89
91
|
rescue TjException => msg
|
90
|
-
error(
|
92
|
+
error('tj3_ivld_freeze_date',
|
93
|
+
"Invalid freeze date: #{msg.message}")
|
91
94
|
end
|
92
95
|
end
|
93
96
|
@opts.on('--freezebytask',
|
@@ -117,6 +120,10 @@ EOT
|
|
117
120
|
'reports.')) do
|
118
121
|
@noReports = true
|
119
122
|
end
|
123
|
+
@opts.on('--add-trace',
|
124
|
+
format('Append a current data set to all trace reports.')) do
|
125
|
+
@generateTraces = true
|
126
|
+
end
|
120
127
|
@opts.on('--abort-on-warnings',
|
121
128
|
format('Abort program on warnings like we do on errors.')) do
|
122
129
|
@abortOnWarning = true
|
@@ -133,49 +140,48 @@ EOT
|
|
133
140
|
end
|
134
141
|
|
135
142
|
def appMain(files)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
tj.maxCpuCores = @maxCpuCores
|
146
|
-
tj.warnTsDeltas = @warnTsDeltas
|
147
|
-
tj.messageHandler.abortOnWarning = @abortOnWarning
|
148
|
-
keepParser = !@timeSheets.empty? || !@statusSheets.empty?
|
149
|
-
return 1 unless tj.parse(files, keepParser)
|
143
|
+
if files.empty?
|
144
|
+
error('tj3_tjp_file_missing',
|
145
|
+
'You must provide at least one .tjp file')
|
146
|
+
end
|
147
|
+
if @outputDir != '' && !File.directory?(@outputDir)
|
148
|
+
error('tj3_outdir_missing',
|
149
|
+
"Output directory #{@outputDir} does not exist or is not " +
|
150
|
+
"a directory!")
|
151
|
+
end
|
150
152
|
|
151
|
-
|
153
|
+
tj = TaskJuggler.new
|
154
|
+
tj.maxCpuCores = @maxCpuCores
|
155
|
+
tj.warnTsDeltas = @warnTsDeltas
|
156
|
+
tj.generateTraces = @generateTraces
|
157
|
+
MessageHandlerInstance.instance.abortOnWarning = @abortOnWarning
|
158
|
+
keepParser = !@timeSheets.empty? || !@statusSheets.empty?
|
159
|
+
return 1 unless tj.parse(files, keepParser)
|
152
160
|
|
153
|
-
|
154
|
-
return 1 unless @forceReports
|
155
|
-
end
|
161
|
+
return 0 if @checkSyntax
|
156
162
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
return 1 if !tj.checkTimeSheet(ts) || tj.errors > 0
|
161
|
-
end
|
162
|
-
@statusSheets.each do |ss|
|
163
|
-
return 1 if !tj.checkStatusSheet(ss) || tj.errors > 0
|
164
|
-
end
|
163
|
+
if !tj.schedule
|
164
|
+
return 1 unless @forceReports
|
165
|
+
end
|
165
166
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
167
|
+
# The checks of time and status sheets is probably only used for
|
168
|
+
# debugging. Normally, this function is provided by tj3client.
|
169
|
+
@timeSheets.each do |ts|
|
170
|
+
return 1 if !tj.checkTimeSheet(ts) || tj.errors > 0
|
171
|
+
end
|
172
|
+
@statusSheets.each do |ss|
|
173
|
+
return 1 if !tj.checkStatusSheet(ss) || tj.errors > 0
|
174
|
+
end
|
171
175
|
|
172
|
-
|
176
|
+
# Check for freeze mode and generate the booking file if requested.
|
177
|
+
if @freeze
|
178
|
+
return 1 unless tj.freeze(@freezeDate, @freezeByTask) &&
|
179
|
+
tj.errors == 0
|
180
|
+
end
|
173
181
|
|
174
|
-
|
182
|
+
return 0 if @noReports
|
175
183
|
|
176
|
-
|
177
|
-
return 1
|
178
|
-
end
|
184
|
+
return 1 if !tj.generateReports(@outputDir) || tj.errors > 0
|
179
185
|
|
180
186
|
0
|
181
187
|
end
|
@@ -14,7 +14,7 @@
|
|
14
14
|
require 'drb'
|
15
15
|
require 'drb/acl'
|
16
16
|
require 'taskjuggler/Tj3AppBase'
|
17
|
-
require 'taskjuggler/
|
17
|
+
require 'taskjuggler/daemon/DaemonConnector'
|
18
18
|
|
19
19
|
# Name of the application
|
20
20
|
AppConfig.appName = 'tj3client'
|
@@ -28,6 +28,8 @@ class TaskJuggler
|
|
28
28
|
# requires a properly configured tj3d to work.
|
29
29
|
class Tj3Client < Tj3AppBase
|
30
30
|
|
31
|
+
include DaemonConnectorMixin
|
32
|
+
|
31
33
|
def initialize
|
32
34
|
super
|
33
35
|
|
@@ -136,8 +138,8 @@ EOT
|
|
136
138
|
@uriFile = arg
|
137
139
|
end
|
138
140
|
@opts.on('-r', '--regexp',
|
139
|
-
format('The report IDs are not fixed but regular
|
140
|
-
'that match a set of reports')) do |arg|
|
141
|
+
format('The report IDs are not fixed but regular ' +
|
142
|
+
'expressions that match a set of reports')) do |arg|
|
141
143
|
@regExpMode = true
|
142
144
|
end
|
143
145
|
@opts.on('--unsafe',
|
@@ -151,8 +153,8 @@ EOT
|
|
151
153
|
format('Request the report to be generated in the specified' +
|
152
154
|
'format. Use multiple options to request multiple ' +
|
153
155
|
'formats. Supported formats are csv, html, niku and ' +
|
154
|
-
'tjp. By default, the formats specified in the
|
155
|
-
'definition are used.')) do |arg|
|
156
|
+
'tjp. By default, the formats specified in the ' +
|
157
|
+
'report definition are used.')) do |arg|
|
156
158
|
@formats = [] unless @formats
|
157
159
|
@formats << arg
|
158
160
|
end
|
@@ -160,21 +162,18 @@ EOT
|
|
160
162
|
end
|
161
163
|
|
162
164
|
def appMain(args)
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
rescue TjRuntimeError
|
176
|
-
return 1
|
177
|
-
end
|
165
|
+
# Run a first check of the non-optional command line arguments.
|
166
|
+
checkCommand(args)
|
167
|
+
# Read some configuration variables. Except for the authKey, they are
|
168
|
+
# all optional.
|
169
|
+
@rc.configure(self, 'global')
|
170
|
+
|
171
|
+
@broker = connectDaemon
|
172
|
+
retVal = executeCommand(args[0], args[1..-1])
|
173
|
+
disconnectDaemon
|
174
|
+
@broker = nil
|
175
|
+
|
176
|
+
retVal
|
178
177
|
end
|
179
178
|
|
180
179
|
private
|
@@ -205,64 +204,7 @@ EOT
|
|
205
204
|
end
|
206
205
|
end
|
207
206
|
|
208
|
-
error(errorMessage)
|
209
|
-
end
|
210
|
-
|
211
|
-
def connectDaemon
|
212
|
-
unless @authKey
|
213
|
-
$stderr.puts <<'EOT'
|
214
|
-
You must set an authentication key in the configuration file. Create a file
|
215
|
-
named .taskjugglerrc or taskjuggler.rc that contains at least the following
|
216
|
-
lines. Replace 'your_secret_key' with some random character sequence.
|
217
|
-
|
218
|
-
_global:
|
219
|
-
authKey: your_secret_key
|
220
|
-
EOT
|
221
|
-
end
|
222
|
-
|
223
|
-
uri = "druby://#{@host}:#{@port}"
|
224
|
-
if @port == 0
|
225
|
-
# If the @port is configured to 0, we need to read the URI to connect
|
226
|
-
# to the server from the .tj3d.uri file that has been generated by the
|
227
|
-
# server.
|
228
|
-
begin
|
229
|
-
uri = File.read(@uriFile).chomp
|
230
|
-
rescue
|
231
|
-
error('The server port is configured to be 0, but no ' +
|
232
|
-
".tj3d.uri file can be found: #{$!}")
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
# We try to play it safe here. The client also starts a DRb server, so
|
237
|
-
# we need to make sure it's constricted to localhost only. We require
|
238
|
-
# the DRb server for the standard IO redirection to work.
|
239
|
-
$SAFE = 1 unless @unsafeMode
|
240
|
-
DRb.install_acl(ACL.new(%w[ deny all
|
241
|
-
allow 127.0.0.1 ]))
|
242
|
-
DRb.start_service('druby://127.0.0.1:0')
|
243
|
-
|
244
|
-
begin
|
245
|
-
# Get the ProjectBroker object from the tj3d.
|
246
|
-
@broker = DRbObject.new(nil, uri)
|
247
|
-
# Client and server should always come from the same Gem. Since we
|
248
|
-
# restict communication to localhost, that's probably not a problem.
|
249
|
-
if (check = @broker.apiVersion(@authKey, 1)) < 0
|
250
|
-
error('This client is too old for the server. Please ' +
|
251
|
-
'upgrade to a more recent version of the software.')
|
252
|
-
elsif check == 0
|
253
|
-
error('Authentication failed. Please check your authentication ' +
|
254
|
-
'key to match the server key.')
|
255
|
-
end
|
256
|
-
rescue
|
257
|
-
error("TaskJuggler server on host '#{@host}' port " +
|
258
|
-
"#{@port} is not responding")
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
def disconnectDaemon
|
263
|
-
@broker = nil
|
264
|
-
|
265
|
-
DRb.stop_service
|
207
|
+
error('tjc_cmd_error', errorMessage)
|
266
208
|
end
|
267
209
|
|
268
210
|
def executeCommand(command, args)
|
@@ -271,22 +213,28 @@ EOT
|
|
271
213
|
$stdout.puts callDaemon(:status, [])
|
272
214
|
when 'terminate'
|
273
215
|
callDaemon(:stop, [])
|
274
|
-
info('Daemon terminated')
|
216
|
+
info('tjc_daemon_term', 'Daemon terminated')
|
275
217
|
when 'add'
|
276
218
|
res = callDaemon(:addProject, [ Dir.getwd, args,
|
277
219
|
$stdout, $stderr, $stdin, @silent ])
|
278
|
-
|
279
|
-
|
220
|
+
if res
|
221
|
+
info('tjc_proj_added', "Project(s) #{args.join(', ')} added")
|
222
|
+
return 0
|
223
|
+
else
|
224
|
+
warning('tjc_proj_adding_failed',
|
225
|
+
"Projects(s) #{args.join(', ')} could not be added")
|
226
|
+
return 1
|
227
|
+
end
|
280
228
|
when 'remove'
|
281
229
|
args.each do |arg|
|
282
230
|
unless callDaemon(:removeProject, arg)
|
283
|
-
error("Project '#{arg}' not found in list")
|
231
|
+
error('tjc_prj_not_found', "Project '#{arg}' not found in list")
|
284
232
|
end
|
285
233
|
end
|
286
|
-
info('Project removed')
|
234
|
+
info('tjc_prj_removed', 'Project removed')
|
287
235
|
when 'update'
|
288
236
|
callDaemon(:update, [])
|
289
|
-
info('Reload requested')
|
237
|
+
info('tjc_reload_req', 'Reload requested')
|
290
238
|
when 'report'
|
291
239
|
# The first value of args is the project ID. The following values
|
292
240
|
# could be either report IDs or TJI file # names ('.' or '*.tji').
|
@@ -298,17 +246,22 @@ EOT
|
|
298
246
|
reportIds, tjiFiles = splitIdsAndFiles(args)
|
299
247
|
if reportIds.empty?
|
300
248
|
disconnectReportServer
|
301
|
-
error('You must provide at least one report ID')
|
249
|
+
error('tjc_no_rep_id', 'You must provide at least one report ID')
|
302
250
|
end
|
303
251
|
# Send the provided .tji files to the ReportServer.
|
304
252
|
failed = !addFiles(tjiFiles)
|
305
253
|
# Ask the ReportServer to generate the reports with the provided IDs.
|
306
254
|
unless failed
|
307
255
|
reportIds.each do |reportId|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
256
|
+
begin
|
257
|
+
unless @reportServer.generateReport(@rs_authKey, reportId,
|
258
|
+
@regExpMode, @formats, nil)
|
259
|
+
failed = true
|
260
|
+
break
|
261
|
+
end
|
262
|
+
rescue
|
263
|
+
error('tjc_gen_rep_failed',
|
264
|
+
"Could not generate report #{reportId}: #{$!}")
|
312
265
|
end
|
313
266
|
end
|
314
267
|
end
|
@@ -334,9 +287,15 @@ EOT
|
|
334
287
|
# Ask the ReportServer to generate the reports with the provided IDs.
|
335
288
|
unless failed
|
336
289
|
reportIds.each do |reportId|
|
337
|
-
|
338
|
-
|
339
|
-
|
290
|
+
begin
|
291
|
+
unless @reportServer.listReports(@rs_authKey, reportId,
|
292
|
+
@regExpMode)
|
293
|
+
failed = true
|
294
|
+
break
|
295
|
+
end
|
296
|
+
rescue
|
297
|
+
error('tjc_report_list_failed',
|
298
|
+
"Getting report list failed: #{$!}")
|
340
299
|
end
|
341
300
|
end
|
342
301
|
end
|
@@ -348,7 +307,7 @@ EOT
|
|
348
307
|
begin
|
349
308
|
res = @reportServer.checkTimeSheet(@rs_authKey, args[1])
|
350
309
|
rescue
|
351
|
-
error("Time sheet check failed: #{$!}")
|
310
|
+
error('tjc_tschck_failed', "Time sheet check failed: #{$!}")
|
352
311
|
end
|
353
312
|
disconnectReportServer
|
354
313
|
return res ? 0 : 1
|
@@ -357,7 +316,7 @@ EOT
|
|
357
316
|
begin
|
358
317
|
res = @reportServer.checkStatusSheet(@rs_authKey, args[1])
|
359
318
|
rescue
|
360
|
-
error("Status sheet check failed: #{$!}")
|
319
|
+
error('tjc_sschck_failed', "Status sheet check failed: #{$!}")
|
361
320
|
end
|
362
321
|
disconnectReportServer
|
363
322
|
return res ? 0 : 1
|
@@ -370,19 +329,19 @@ EOT
|
|
370
329
|
def connectToReportServer(projectId)
|
371
330
|
@ps_uri, @ps_authKey = callDaemon(:getProject, projectId)
|
372
331
|
if @ps_uri.nil?
|
373
|
-
error("No project with ID #{projectId} loaded")
|
332
|
+
error('tjc_prj_id_not_loaded', "No project with ID #{projectId} loaded")
|
374
333
|
end
|
375
334
|
begin
|
376
335
|
@projectServer = DRbObject.new(nil, @ps_uri)
|
377
336
|
@rs_uri, @rs_authKey = @projectServer.getReportServer(@ps_authKey)
|
378
337
|
@reportServer = DRbObject.new(nil, @rs_uri)
|
379
338
|
rescue
|
380
|
-
error("Cannot get report server")
|
339
|
+
error('tjc_no_rep_srv', "Cannot get report server: #{$!}")
|
381
340
|
end
|
382
341
|
begin
|
383
342
|
@reportServer.connect(@rs_authKey, $stdout, $stderr, $stdin, @silent)
|
384
343
|
rescue
|
385
|
-
error("Can't connect IO: #{$!}")
|
344
|
+
error('tjc_no_io_connect', "Can't connect IO: #{$!}")
|
386
345
|
end
|
387
346
|
end
|
388
347
|
|
@@ -390,12 +349,12 @@ EOT
|
|
390
349
|
begin
|
391
350
|
@reportServer.disconnect(@rs_authKey)
|
392
351
|
rescue
|
393
|
-
error("Can't disconnect IO: #{$!}")
|
352
|
+
error('tjc_no_io_disconnect', "Can't disconnect IO: #{$!}")
|
394
353
|
end
|
395
354
|
begin
|
396
355
|
@reportServer.terminate(@rs_authKey)
|
397
356
|
rescue
|
398
|
-
error("Report server termination failed: #{$!}")
|
357
|
+
error('tjc_srv_term_failed', "Report server termination failed: #{$!}")
|
399
358
|
end
|
400
359
|
@reportServer = nil
|
401
360
|
@rs_uri = nil
|
@@ -411,7 +370,8 @@ EOT
|
|
411
370
|
begin
|
412
371
|
return @broker.command(@authKey, command, args)
|
413
372
|
rescue
|
414
|
-
error(
|
373
|
+
error('tjc_call_srv_failed',
|
374
|
+
"Call to TaskJuggler server on host '#{@host}' " +
|
415
375
|
"port #{@port} failed: #{$!}")
|
416
376
|
end
|
417
377
|
end
|
@@ -445,23 +405,13 @@ EOT
|
|
445
405
|
return false
|
446
406
|
end
|
447
407
|
rescue
|
448
|
-
error(
|
408
|
+
error('tjc_canont_add_file',
|
409
|
+
"Cannot add file #{file} to ReportServer")
|
449
410
|
end
|
450
411
|
end
|
451
412
|
true
|
452
413
|
end
|
453
414
|
|
454
|
-
def info(message)
|
455
|
-
return if @silent
|
456
|
-
$stdout.puts "#{message}"
|
457
|
-
end
|
458
|
-
|
459
|
-
def error(message, exitVal = 1)
|
460
|
-
$stderr.puts "ERROR: #{message}"
|
461
|
-
# Don't call exit in unsafe mode. Raise a StandardError instead.
|
462
|
-
raise TjRuntimeError
|
463
|
-
end
|
464
|
-
|
465
415
|
end
|
466
416
|
|
467
417
|
end
|
@@ -13,7 +13,7 @@
|
|
13
13
|
|
14
14
|
require 'drb'
|
15
15
|
require 'taskjuggler/Tj3AppBase'
|
16
|
-
require 'taskjuggler/
|
16
|
+
require 'taskjuggler/MessageHandler'
|
17
17
|
require 'taskjuggler/daemon/ProjectBroker'
|
18
18
|
|
19
19
|
# Name of the application
|
@@ -27,14 +27,17 @@ class TaskJuggler
|
|
27
27
|
super
|
28
28
|
@mandatoryArgs = '[<tjp file> [<tji file> ...] ...]'
|
29
29
|
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
30
|
+
@mhi = MessageHandlerInstance.instance
|
31
|
+
@mhi.logFile = File.join(Dir.getwd, "/#{AppConfig.appName}.log")
|
32
|
+
@mhi.appName = AppConfig.appName
|
33
|
+
# By default show only warnings and more serious messages.
|
34
|
+
@mhi.outputLevel = :warning
|
33
35
|
@daemonize = true
|
34
36
|
@uriFile = File.join(Dir.getwd, '.tj3d.uri')
|
35
37
|
@port = nil
|
36
38
|
@webServer = false
|
37
|
-
@webServerPort =
|
39
|
+
@webServerPort = 8080
|
40
|
+
@webdPidFile = File.join(Dir.getwd, ".tj3webd-#{$$}.pid").untaint
|
38
41
|
end
|
39
42
|
|
40
43
|
def processArguments(argv)
|
@@ -69,33 +72,65 @@ EOT
|
|
69
72
|
'requests (Default: 8080).')) do |arg|
|
70
73
|
@webServerPort = arg
|
71
74
|
end
|
75
|
+
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
75
79
|
def appMain(files)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
80
|
+
broker = ProjectBroker.new
|
81
|
+
@rc.configure(self, 'global')
|
82
|
+
@rc.configure(@mhi, 'global.log')
|
83
|
+
@rc.configure(broker, 'global')
|
84
|
+
@rc.configure(broker, 'daemon')
|
85
|
+
|
86
|
+
# Set some config variables if corresponding data was provided via the
|
87
|
+
# command line.
|
88
|
+
broker.port = @port if @port
|
89
|
+
broker.uriFile = @uriFile.untaint
|
90
|
+
broker.projectFiles = sortInputFiles(files) unless files.empty?
|
91
|
+
broker.daemonize = @daemonize
|
92
|
+
# Create log files for standard IO for each child process if the daemon
|
93
|
+
# is not disconnected from the terminal.
|
94
|
+
broker.logStdIO = !@daemonize
|
95
|
+
|
96
|
+
if @webServer
|
97
|
+
webdCommand = "tj3webd --webserver-port #{@webServerPort} " +
|
98
|
+
"--pidfile #{@webdPidFile}"
|
99
|
+
# Also start the web server as a separate process. We keep the PID, so
|
100
|
+
# we can terminate that process again when we exit the daemon.
|
101
|
+
begin
|
102
|
+
`#{webdCommand}`
|
103
|
+
rescue
|
104
|
+
error('tj3webd_start_failed', "Could not start tj3webd: #{$!}")
|
105
|
+
end
|
106
|
+
info('web_server_started', "Web server started as '#{webdCommand}'")
|
98
107
|
end
|
108
|
+
|
109
|
+
broker.start
|
110
|
+
|
111
|
+
if @webServer
|
112
|
+
pid = nil
|
113
|
+
begin
|
114
|
+
# Read the PID of the web server from the PID file.
|
115
|
+
File.open(@webdPidFile, 'r') do |f|
|
116
|
+
pid = f.read.to_i
|
117
|
+
end
|
118
|
+
rescue
|
119
|
+
warning('cannot_read_webd_pidfile',
|
120
|
+
"Cannot read tj3webd PID file (#{@webdPidFile}): #{$!}")
|
121
|
+
end
|
122
|
+
# If we have started the web server, we are also trying to terminate
|
123
|
+
# that process again.
|
124
|
+
begin
|
125
|
+
Process.kill("TERM", pid)
|
126
|
+
rescue
|
127
|
+
warning('tj3webd_term_failed',
|
128
|
+
"Could not terminate web server: #{$!}")
|
129
|
+
end
|
130
|
+
info('web_server_terminated', "Web server with PID #{pid} terminated")
|
131
|
+
end
|
132
|
+
|
133
|
+
0
|
99
134
|
end
|
100
135
|
|
101
136
|
private
|
@@ -128,11 +163,13 @@ EOT
|
|
128
163
|
# .tji files are optional. But if they are specified, they must
|
129
164
|
# always follow the master file in the list.
|
130
165
|
if project.nil?
|
131
|
-
error(
|
166
|
+
error('tj3d_tji_before_tjp',
|
167
|
+
"You must specify a '.tjp' file before the '.tji' files")
|
132
168
|
end
|
133
169
|
project << file
|
134
170
|
else
|
135
|
-
error(
|
171
|
+
error('tj3d_no_file_Ext',
|
172
|
+
"Project files must have a '.tjp' or '.tji' extension")
|
136
173
|
end
|
137
174
|
end
|
138
175
|
|