taskjuggler 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. data/CHANGELOG +44 -0
  2. data/bin/tj3webd +4 -0
  3. data/data/css/tjreport.css +14 -5
  4. data/data/tjp.vim +22 -7
  5. data/examples/Fedora-20/reports.tji +2 -4
  6. data/examples/Scrum/Product Burndown.csv +26 -0
  7. data/examples/Scrum/Sprint 1 Burndown.csv +26 -0
  8. data/examples/Scrum/Sprint 2 Burndown.csv +26 -0
  9. data/examples/Scrum/Sprint 3 Burndown.csv +26 -0
  10. data/examples/Scrum/scrum.tjp +141 -0
  11. data/examples/Tutorial/tutorial.tjp +13 -7
  12. data/lib/taskjuggler/Attributes.rb +2 -3
  13. data/lib/taskjuggler/HTMLDocument.rb +25 -18
  14. data/lib/taskjuggler/Journal.rb +85 -65
  15. data/lib/taskjuggler/KeywordDocumentation.rb +25 -13
  16. data/lib/taskjuggler/LeaveList.rb +1 -1
  17. data/lib/taskjuggler/Limits.rb +3 -5
  18. data/lib/taskjuggler/MessageHandler.rb +173 -19
  19. data/lib/taskjuggler/Painter.rb +75 -0
  20. data/lib/taskjuggler/Painter/BasicShapes.rb +76 -0
  21. data/lib/taskjuggler/Painter/Color.rb +273 -0
  22. data/lib/taskjuggler/Painter/Element.rb +56 -0
  23. data/lib/taskjuggler/Painter/FontData.rb +221 -0
  24. data/lib/taskjuggler/Painter/FontMetrics.rb +125 -0
  25. data/lib/taskjuggler/Painter/FontMetricsData.rb +151 -0
  26. data/lib/taskjuggler/Painter/Group.rb +77 -0
  27. data/lib/taskjuggler/Painter/Points.rb +47 -0
  28. data/lib/taskjuggler/Painter/Primitives.rb +100 -0
  29. data/lib/taskjuggler/Painter/SVGSupport.rb +36 -0
  30. data/lib/taskjuggler/Painter/Text.rb +36 -0
  31. data/lib/taskjuggler/Project.rb +46 -29
  32. data/lib/taskjuggler/ProjectFileParser.rb +24 -22
  33. data/lib/taskjuggler/ProjectFileScanner.rb +2 -2
  34. data/lib/taskjuggler/PropertyTreeNode.rb +26 -34
  35. data/lib/taskjuggler/Query.rb +8 -5
  36. data/lib/taskjuggler/RealFormat.rb +3 -0
  37. data/lib/taskjuggler/Resource.rb +3 -5
  38. data/lib/taskjuggler/ResourceScenario.rb +19 -7
  39. data/lib/taskjuggler/RichText.rb +4 -6
  40. data/lib/taskjuggler/RichText/FunctionExample.rb +1 -1
  41. data/lib/taskjuggler/RichText/FunctionHandler.rb +5 -6
  42. data/lib/taskjuggler/RichText/Parser.rb +4 -6
  43. data/lib/taskjuggler/RichText/RTFNavigator.rb +1 -1
  44. data/lib/taskjuggler/RichText/RTFQuery.rb +2 -3
  45. data/lib/taskjuggler/RichText/RTFReport.rb +1 -1
  46. data/lib/taskjuggler/RichText/RTFReportLink.rb +1 -2
  47. data/lib/taskjuggler/RichText/RTFWithQuerySupport.rb +1 -1
  48. data/lib/taskjuggler/RichText/Scanner.rb +6 -6
  49. data/lib/taskjuggler/RichText/Snip.rb +1 -2
  50. data/lib/taskjuggler/RuntimeConfig.rb +9 -6
  51. data/lib/taskjuggler/ScenarioData.rb +4 -3
  52. data/lib/taskjuggler/Scoreboard.rb +6 -0
  53. data/lib/taskjuggler/SheetHandlerBase.rb +25 -8
  54. data/lib/taskjuggler/SimpleQueryExpander.rb +14 -5
  55. data/lib/taskjuggler/SyntaxReference.rb +1 -2
  56. data/lib/taskjuggler/TableColumnSorter.rb +84 -0
  57. data/lib/taskjuggler/Task.rb +3 -5
  58. data/lib/taskjuggler/TaskJuggler.rb +36 -29
  59. data/lib/taskjuggler/TaskScenario.rb +154 -66
  60. data/lib/taskjuggler/TextParser.rb +24 -17
  61. data/lib/taskjuggler/TextParser/Scanner.rb +16 -11
  62. data/lib/taskjuggler/TextParser/SourceFileInfo.rb +20 -15
  63. data/lib/taskjuggler/TimeSheets.rb +6 -12
  64. data/lib/taskjuggler/Tj3AppBase.rb +35 -16
  65. data/lib/taskjuggler/Tj3Config.rb +1 -1
  66. data/lib/taskjuggler/TjpSyntaxRules.rb +239 -49
  67. data/lib/taskjuggler/XMLElement.rb +9 -2
  68. data/lib/taskjuggler/apps/Tj3.rb +43 -37
  69. data/lib/taskjuggler/apps/Tj3Client.rb +62 -112
  70. data/lib/taskjuggler/apps/Tj3Daemon.rb +66 -29
  71. data/lib/taskjuggler/apps/Tj3Man.rb +5 -5
  72. data/lib/taskjuggler/apps/Tj3SsReceiver.rb +10 -13
  73. data/lib/taskjuggler/apps/Tj3SsSender.rb +13 -16
  74. data/lib/taskjuggler/apps/Tj3TsReceiver.rb +10 -13
  75. data/lib/taskjuggler/apps/Tj3TsSender.rb +12 -15
  76. data/lib/taskjuggler/apps/Tj3TsSummary.rb +12 -15
  77. data/lib/taskjuggler/apps/Tj3WebD.rb +99 -0
  78. data/lib/taskjuggler/daemon/Daemon.rb +50 -10
  79. data/lib/taskjuggler/daemon/DaemonConnector.rb +127 -0
  80. data/lib/taskjuggler/daemon/ProcessIntercom.rb +36 -21
  81. data/lib/taskjuggler/daemon/ProjectBroker.rb +122 -112
  82. data/lib/taskjuggler/daemon/ProjectServer.rb +78 -46
  83. data/lib/taskjuggler/daemon/ReportServer.rb +52 -28
  84. data/lib/taskjuggler/daemon/ReportServlet.rb +92 -21
  85. data/lib/taskjuggler/daemon/WebServer.rb +75 -22
  86. data/lib/taskjuggler/daemon/WelcomePage.rb +1 -0
  87. data/lib/taskjuggler/reports/AccountListRE.rb +3 -3
  88. data/lib/taskjuggler/reports/CSVFile.rb +9 -2
  89. data/lib/taskjuggler/reports/ChartPlotter.rb +453 -0
  90. data/lib/taskjuggler/reports/Navigator.rb +1 -0
  91. data/lib/taskjuggler/reports/NikuReport.rb +4 -4
  92. data/lib/taskjuggler/reports/Report.rb +6 -18
  93. data/lib/taskjuggler/reports/ReportBase.rb +9 -9
  94. data/lib/taskjuggler/reports/ReportContext.rb +2 -2
  95. data/lib/taskjuggler/reports/StatusSheetReport.rb +6 -6
  96. data/lib/taskjuggler/reports/TableReport.rb +24 -15
  97. data/lib/taskjuggler/reports/TimeSheetReport.rb +5 -5
  98. data/lib/taskjuggler/reports/TraceReport.rb +251 -0
  99. data/lib/tj3webd.rb +17 -0
  100. data/manual/Day_To_Day_Juggling +10 -3
  101. data/manual/Installation +38 -19
  102. data/manual/Software +25 -19
  103. data/manual/Tutorial +119 -110
  104. data/manual/html/Day_To_Day_Juggling.html +7 -5
  105. data/manual/html/Getting_Started.html +4 -4
  106. data/manual/html/How_To_Contribute.html +4 -4
  107. data/manual/html/Installation.html +19 -11
  108. data/manual/html/Intro.html +4 -4
  109. data/manual/html/Reporting_Bugs.html +4 -4
  110. data/manual/html/Rich_Text_Attributes.html +4 -4
  111. data/manual/html/Software.html +15 -11
  112. data/manual/html/TaskJuggler_2x_Migration.html +4 -4
  113. data/manual/html/TaskJuggler_Internals.html +4 -4
  114. data/manual/html/The_TaskJuggler_Syntax.html +4 -4
  115. data/manual/html/Tutorial.html +41 -32
  116. data/manual/html/account.html +4 -4
  117. data/manual/html/account.task.html +4 -4
  118. data/manual/html/accountprefix.html +4 -4
  119. data/manual/html/accountreport.html +27 -9
  120. data/manual/html/accountroot.html +5 -5
  121. data/manual/html/active.html +4 -4
  122. data/manual/html/adopt.task.html +4 -4
  123. data/manual/html/aggregate.html +4 -4
  124. data/manual/html/alert.html +4 -4
  125. data/manual/html/alertlevels.html +4 -4
  126. data/manual/html/allocate.html +5 -5
  127. data/manual/html/alphabet.html +4 -4
  128. data/manual/html/alternative.html +4 -4
  129. data/manual/html/author.html +4 -4
  130. data/manual/html/balance.html +5 -5
  131. data/manual/html/booking.resource.html +4 -4
  132. data/manual/html/booking.task.html +4 -4
  133. data/manual/html/caption.html +5 -5
  134. data/manual/html/cellcolor.column.html +43 -8
  135. data/manual/html/celltext.column.html +4 -4
  136. data/manual/html/center.html +5 -5
  137. data/manual/html/charge.html +4 -4
  138. data/manual/html/chargeset.html +4 -4
  139. data/manual/html/columnid.html +27 -15
  140. data/manual/html/columns.html +5 -5
  141. data/manual/html/complete.html +4 -4
  142. data/manual/html/copyright.html +4 -4
  143. data/manual/html/credits.html +4 -4
  144. data/manual/html/css/tjreport.css +14 -5
  145. data/manual/html/currency.html +4 -4
  146. data/manual/html/currencyformat.html +5 -5
  147. data/manual/html/dailymax.html +5 -5
  148. data/manual/html/dailymin.html +5 -5
  149. data/manual/html/dailyworkinghours.html +4 -4
  150. data/manual/html/date.extend.html +4 -4
  151. data/manual/html/date.html +5 -5
  152. data/manual/html/definitions.html +4 -4
  153. data/manual/html/depends.html +4 -4
  154. data/manual/html/details.html +4 -4
  155. data/manual/html/disabled.html +4 -4
  156. data/manual/html/duration.html +4 -4
  157. data/manual/html/efficiency.html +4 -4
  158. data/manual/html/effort.html +4 -4
  159. data/manual/html/email.html +4 -4
  160. data/manual/html/enabled.html +4 -4
  161. data/manual/html/end.column.html +4 -4
  162. data/manual/html/end.html +4 -4
  163. data/manual/html/end.limit.html +4 -4
  164. data/manual/html/end.report.html +5 -5
  165. data/manual/html/end.timesheet.html +4 -4
  166. data/manual/html/endcredit.html +4 -4
  167. data/manual/html/epilog.html +5 -5
  168. data/manual/html/export.html +4 -4
  169. data/manual/html/extend.html +4 -4
  170. data/manual/html/fail.html +43 -8
  171. data/manual/html/fdl.html +4 -4
  172. data/manual/html/flags.account.html +4 -4
  173. data/manual/html/flags.html +4 -4
  174. data/manual/html/flags.journalentry.html +4 -4
  175. data/manual/html/flags.report.html +5 -5
  176. data/manual/html/flags.resource.html +4 -4
  177. data/manual/html/flags.statussheet.html +4 -4
  178. data/manual/html/flags.task.html +4 -4
  179. data/manual/html/flags.timesheet.html +4 -4
  180. data/manual/html/fontcolor.column.html +43 -8
  181. data/manual/html/footer.html +5 -5
  182. data/manual/html/formats.html +5 -5
  183. data/manual/html/functions.html +4 -4
  184. data/manual/html/gapduration.html +4 -4
  185. data/manual/html/gaplength.html +4 -4
  186. data/manual/html/halign.center.html +4 -4
  187. data/manual/html/halign.column.html +43 -8
  188. data/manual/html/halign.left.html +4 -4
  189. data/manual/html/halign.right.html +4 -4
  190. data/manual/html/hasalert.html +4 -4
  191. data/manual/html/header.html +5 -5
  192. data/manual/html/headline.html +7 -7
  193. data/manual/html/height.html +72 -0
  194. data/manual/html/hideaccount.html +46 -11
  195. data/manual/html/hidejournalentry.html +5 -5
  196. data/manual/html/hidereport.html +43 -8
  197. data/manual/html/hideresource.html +44 -9
  198. data/manual/html/hidetask.html +44 -9
  199. data/manual/html/icalreport.html +4 -4
  200. data/manual/html/include.macro.html +4 -4
  201. data/manual/html/include.project.html +4 -4
  202. data/manual/html/include.properties.html +4 -4
  203. data/manual/html/index.html +2 -1
  204. data/manual/html/inherit.extend.html +4 -4
  205. data/manual/html/interval1.html +4 -4
  206. data/manual/html/interval2.html +4 -4
  207. data/manual/html/interval3.html +4 -4
  208. data/manual/html/interval4.html +4 -4
  209. data/manual/html/isactive.html +4 -4
  210. data/manual/html/ischildof.html +4 -4
  211. data/manual/html/isdependencyof.html +4 -4
  212. data/manual/html/isdutyof.html +4 -4
  213. data/manual/html/isfeatureof.html +4 -4
  214. data/manual/html/isleaf.html +4 -4
  215. data/manual/html/ismilestone.html +4 -4
  216. data/manual/html/isongoing.html +4 -4
  217. data/manual/html/isresource.html +4 -4
  218. data/manual/html/isresponsibilityof.html +4 -4
  219. data/manual/html/istask.html +4 -4
  220. data/manual/html/journalattributes.html +11 -7
  221. data/manual/html/journalentry.html +4 -4
  222. data/manual/html/journalmode.html +5 -5
  223. data/manual/html/leaveallowance.html +5 -5
  224. data/manual/html/leaves.html +5 -6
  225. data/manual/html/left.html +5 -5
  226. data/manual/html/length.html +4 -4
  227. data/manual/html/limits.allocate.html +4 -4
  228. data/manual/html/limits.html +4 -4
  229. data/manual/html/limits.resource.html +4 -4
  230. data/manual/html/limits.task.html +4 -4
  231. data/manual/html/listitem.column.html +4 -4
  232. data/manual/html/listtype.column.html +4 -4
  233. data/manual/html/loadunit.html +5 -5
  234. data/manual/html/logicalexpression.html +8 -44
  235. data/manual/html/logicalflagexpression.html +4 -4
  236. data/manual/html/macro.html +4 -4
  237. data/manual/html/managers.html +4 -4
  238. data/manual/html/mandatory.html +4 -4
  239. data/manual/html/maxend.html +4 -4
  240. data/manual/html/maximum.html +5 -5
  241. data/manual/html/maxstart.html +4 -4
  242. data/manual/html/milestone.html +4 -4
  243. data/manual/html/minend.html +4 -4
  244. data/manual/html/minimum.html +5 -5
  245. data/manual/html/minstart.html +4 -4
  246. data/manual/html/monthlymax.html +5 -5
  247. data/manual/html/monthlymin.html +5 -5
  248. data/manual/html/navbar.html +10 -4
  249. data/manual/html/navigator.html +4 -4
  250. data/manual/html/newtask.html +4 -4
  251. data/manual/html/nikureport.html +4 -4
  252. data/manual/html/note.task.html +4 -4
  253. data/manual/html/now.html +4 -4
  254. data/manual/html/numberformat.html +5 -5
  255. data/manual/html/onend.html +4 -4
  256. data/manual/html/onstart.html +4 -4
  257. data/manual/html/opennodes.html +5 -5
  258. data/manual/html/overtime.booking.html +4 -4
  259. data/manual/html/period.column.html +4 -4
  260. data/manual/html/period.limit.html +4 -4
  261. data/manual/html/period.report.html +5 -5
  262. data/manual/html/period.task.html +4 -4
  263. data/manual/html/persistent.html +4 -4
  264. data/manual/html/precedes.html +4 -4
  265. data/manual/html/priority.html +4 -4
  266. data/manual/html/priority.timesheet.html +4 -4
  267. data/manual/html/project.html +4 -4
  268. data/manual/html/projectid.html +4 -4
  269. data/manual/html/projectid.task.html +4 -4
  270. data/manual/html/projectids.html +4 -4
  271. data/manual/html/projection.html +5 -7
  272. data/manual/html/prolog.html +5 -5
  273. data/manual/html/properties.html +11 -5
  274. data/manual/html/purge.html +5 -5
  275. data/manual/html/rate.html +4 -4
  276. data/manual/html/rate.resource.html +4 -4
  277. data/manual/html/reference.extend.html +4 -4
  278. data/manual/html/remaining.html +4 -4
  279. data/manual/html/replace.html +4 -4
  280. data/manual/html/reportprefix.html +4 -4
  281. data/manual/html/resource.html +4 -10
  282. data/manual/html/resourceattributes.html +4 -4
  283. data/manual/html/resourceprefix.html +4 -4
  284. data/manual/html/resourcereport.html +28 -10
  285. data/manual/html/resourceroot.html +5 -5
  286. data/manual/html/resources.limit.html +4 -4
  287. data/manual/html/responsible.html +4 -4
  288. data/manual/html/richtext.extend.html +4 -4
  289. data/manual/html/right.html +5 -5
  290. data/manual/html/rollupaccount.html +44 -9
  291. data/manual/html/rollupresource.html +44 -9
  292. data/manual/html/rolluptask.html +44 -9
  293. data/manual/html/scale.column.html +4 -4
  294. data/manual/html/scenario.html +4 -22
  295. data/manual/html/scenario.ical.html +4 -4
  296. data/manual/html/scenarios.export.html +4 -4
  297. data/manual/html/scenarios.html +5 -5
  298. data/manual/html/scenariospecific.extend.html +4 -4
  299. data/manual/html/scheduled.html +4 -4
  300. data/manual/html/scheduling.html +4 -4
  301. data/manual/html/select.html +4 -4
  302. data/manual/html/selfcontained.html +5 -5
  303. data/manual/html/shift.allocate.html +4 -4
  304. data/manual/html/shift.html +4 -4
  305. data/manual/html/shift.resource.html +5 -5
  306. data/manual/html/shift.task.html +4 -4
  307. data/manual/html/shift.timesheet.html +4 -4
  308. data/manual/html/shifts.allocate.html +4 -4
  309. data/manual/html/shifts.resource.html +4 -4
  310. data/manual/html/shifts.task.html +4 -4
  311. data/manual/html/shorttimeformat.html +4 -4
  312. data/manual/html/sloppy.booking.html +4 -4
  313. data/manual/html/sloppy.projection.html +5 -5
  314. data/manual/html/sortaccounts.html +5 -5
  315. data/manual/html/sortjournalentries.html +5 -5
  316. data/manual/html/sortresources.html +5 -5
  317. data/manual/html/sorttasks.html +5 -5
  318. data/manual/html/start.column.html +4 -4
  319. data/manual/html/start.html +4 -4
  320. data/manual/html/start.limit.html +4 -4
  321. data/manual/html/start.report.html +5 -5
  322. data/manual/html/startcredit.html +4 -4
  323. data/manual/html/status.statussheet.html +4 -4
  324. data/manual/html/status.timesheet.html +4 -4
  325. data/manual/html/statussheet.html +4 -4
  326. data/manual/html/statussheetreport.html +4 -4
  327. data/manual/html/strict.projection.html +5 -5
  328. data/manual/html/summary.html +4 -4
  329. data/manual/html/supplement.html +4 -4
  330. data/manual/html/supplement.resource.html +4 -10
  331. data/manual/html/supplement.task.html +4 -28
  332. data/manual/html/tagfile.html +4 -4
  333. data/manual/html/task.html +4 -28
  334. data/manual/html/task.statussheet.html +4 -4
  335. data/manual/html/task.timesheet.html +4 -4
  336. data/manual/html/taskattributes.html +4 -4
  337. data/manual/html/taskprefix.html +4 -4
  338. data/manual/html/taskreport.html +28 -10
  339. data/manual/html/taskroot.html +5 -5
  340. data/manual/html/text.extend.html +4 -4
  341. data/manual/html/textreport.html +27 -9
  342. data/manual/html/timeformat.html +5 -5
  343. data/manual/html/timeoff.nikureport.html +4 -4
  344. data/manual/html/timesheet.html +4 -4
  345. data/manual/html/timesheetreport.html +23 -5
  346. data/manual/html/timezone.export.html +4 -4
  347. data/manual/html/timezone.html +4 -4
  348. data/manual/html/timezone.report.html +5 -5
  349. data/manual/html/timezone.shift.html +4 -4
  350. data/manual/html/timingresolution.html +4 -4
  351. data/manual/html/title.column.html +4 -4
  352. data/manual/html/title.html +5 -5
  353. data/manual/html/toc.html +207 -179
  354. data/manual/html/tooltip.column.html +45 -10
  355. data/manual/html/tracereport.html +405 -0
  356. data/manual/html/trackingscenario.html +6 -6
  357. data/manual/html/treelevel.html +4 -4
  358. data/manual/html/vacation.html +4 -4
  359. data/manual/html/vacation.resource.html +4 -4
  360. data/manual/html/vacation.shift.html +4 -4
  361. data/manual/html/warn.html +43 -8
  362. data/manual/html/weeklymax.html +5 -5
  363. data/manual/html/weeklymin.html +5 -5
  364. data/manual/html/weekstartsmonday.html +4 -4
  365. data/manual/html/weekstartssunday.html +6 -6
  366. data/manual/html/width.column.html +6 -6
  367. data/manual/html/width.html +72 -0
  368. data/manual/html/work.html +4 -4
  369. data/manual/html/workinghours.project.html +4 -4
  370. data/manual/html/workinghours.resource.html +4 -4
  371. data/manual/html/workinghours.shift.html +4 -4
  372. data/manual/html/yearlyworkingdays.html +4 -4
  373. data/spec/Color_spec.rb +60 -0
  374. data/spec/ProjectBroker_spec.rb +3 -2
  375. data/spec/StatusSheets_spec.rb +5 -4
  376. data/spec/TableColumnSorter_spec.rb +78 -0
  377. data/spec/TimeSheets_spec.rb +6 -2
  378. data/spec/Tj3Daemon_spec.rb +2 -2
  379. data/spec/TraceReport_spec.rb +117 -0
  380. data/taskjuggler.gemspec +1 -1
  381. data/test/MessageChecker.rb +3 -1
  382. data/test/ReferenceGenerator.rb +1 -1
  383. data/test/TestSuite/CSV-Reports/Leave.tjp +1 -1
  384. data/test/TestSuite/CSV-Reports/refs/resourcereport_with_tasks.csv +3 -0
  385. data/test/TestSuite/CSV-Reports/refs/taskcounter.csv +9 -0
  386. data/test/TestSuite/CSV-Reports/refs/taskreport_with_resources.csv +19 -16
  387. data/test/TestSuite/CSV-Reports/refs/weekly.csv +1 -0
  388. data/test/TestSuite/Export-Reports/refs/LogicalExpression.tjp +14 -2
  389. data/test/TestSuite/Export-Reports/refs/tutorial.tjp +98 -86
  390. data/test/TestSuite/Scheduler/Correct/Leaves.tjp +25 -0
  391. data/test/TestSuite/Syntax/Correct/Leave.tjp +1 -1
  392. data/test/TestSuite/Syntax/Correct/LogicalExpression.tjp +9 -1
  393. data/test/TestSuite/Syntax/Correct/TraceReport.tjp +10 -0
  394. data/test/TestSuite/Syntax/Correct/tutorial.tjp +10 -4
  395. data/test/test_CSV-Reports.rb +3 -3
  396. data/test/test_Export-Reports.rb +91 -86
  397. data/test/test_Journal.rb +15 -12
  398. data/test/test_Limits.rb +3 -3
  399. data/test/test_Project.rb +1 -2
  400. data/test/test_ProjectFileScanner.rb +1 -1
  401. data/test/test_PropertySet.rb +1 -1
  402. data/test/test_Query.rb +5 -6
  403. data/test/test_ReportGenerator.rb +15 -7
  404. data/test/test_RichText.rb +4 -3
  405. data/test/test_Scheduler.rb +19 -7
  406. data/test/test_ShiftAssignments.rb +2 -2
  407. data/test/test_SimpleQueryExpander.rb +29 -2
  408. data/test/test_Syntax.rb +14 -5
  409. metadata +49 -10
  410. 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 "Attribute name (#{n}) or value (#{v}) may not be nil"
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
 
@@ -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("Invalid freeze date: #{msg.message}")
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
- begin
137
- if files.empty?
138
- error('You must provide at least one .tjp file', 1)
139
- end
140
- if @outputDir != '' && !Dir.exists?(@outputDir)
141
- error("Output directory #{@outputDir} does not exist!")
142
- end
143
-
144
- tj = TaskJuggler.new(true)
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
- return 0 if @checkSyntax
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
- if !tj.schedule
154
- return 1 unless @forceReports
155
- end
161
+ return 0 if @checkSyntax
156
162
 
157
- # The checks of time and status sheets is probably only used for
158
- # debugging. Normally, this function is provided by tj3client.
159
- @timeSheets.each do |ts|
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
- # Check for freeze mode and generate the booking file if requested.
167
- if @freeze
168
- return 1 unless tj.freeze(@freezeDate, @freezeByTask) &&
169
- tj.errors == 0
170
- end
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
- return 0 if @noReports
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
- return 1 if !tj.generateReports(@outputDir) || tj.errors > 0
182
+ return 0 if @noReports
175
183
 
176
- rescue TjRuntimeError
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/LogFile'
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 expressions ' +
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 report ' +
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
- begin
164
- # Run a first check of the non-optional command line arguments.
165
- checkCommand(args)
166
- # Read some configuration variables. Except for the authKey, they are
167
- # all optional.
168
- @rc.configure(self, 'global')
169
-
170
- connectDaemon
171
- retVal = executeCommand(args[0], args[1..-1])
172
- disconnectDaemon
173
-
174
- return retVal
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
- info("Project(s) #{args.join(', ')} added")
279
- return res ? 0 : 1
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
- unless @reportServer.generateReport(@rs_authKey, reportId,
309
- @regExpMode, @formats, nil)
310
- failed = true
311
- break
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
- unless @reportServer.listReports(@rs_authKey, reportId, @regExpMode)
338
- failed = true
339
- break
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("Call to TaskJuggler server on host '#{@host}' " +
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("Cannot add file #{file} to ReportServer")
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/LogFile'
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
- @log = LogFile.instance
31
- @log.logFile = File.join(Dir.getwd, "/#{AppConfig.appName}.log")
32
- @log.appName = AppConfig.appName
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 = nil
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
- begin
77
- broker = ProjectBroker.new
78
- @rc.configure(self, 'global')
79
- @rc.configure(@log, 'global.log')
80
- @rc.configure(broker, 'global')
81
- @rc.configure(broker, 'daemon')
82
-
83
- # Set some config variables if corresponding data was provided via the
84
- # command line.
85
- broker.port = @port if @port
86
- broker.uriFile = @uriFile.untaint
87
- broker.enableWebServer = @webServer
88
- broker.webServerPort = @webServerPort if @webServerPort
89
- broker.projectFiles = sortInputFiles(files) unless files.empty?
90
- broker.daemonize = @daemonize
91
- # Create log files for standard IO for each child process if the daemon
92
- # is not disconnected from the terminal.
93
- broker.logStdIO = !@daemonize
94
-
95
- return broker.start
96
- rescue TjRuntimeError
97
- return 1
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("You must specify a '.tjp' file before the '.tji' files")
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("Project files must have a '.tjp' or '.tji' extension")
171
+ error('tj3d_no_file_Ext',
172
+ "Project files must have a '.tjp' or '.tji' extension")
136
173
  end
137
174
  end
138
175