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
@@ -228,6 +228,7 @@ class TaskJuggler
228
228
  end
229
229
  end
230
230
  end
231
+ nil
231
232
  end
232
233
 
233
234
  end
@@ -306,8 +306,8 @@ EOT
306
306
  'journalMode' => a('journalMode'),
307
307
  'journalAttributes' => a('journalAttributes'),
308
308
  'sortJournalEntries' => a('sortJournalEntries'),
309
- 'costAccount' => a('costAccount'),
310
- 'revenueAccount' => a('revenueAccount') }
309
+ 'costAccount' => a('costaccount'),
310
+ 'revenueAccount' => a('revenueaccount') }
311
311
  query = Query.new(queryAttrs)
312
312
 
313
313
  # Calculate the number of working days in the report interval.
@@ -440,8 +440,8 @@ EOT
440
440
  'journalMode' => a('journalMode'),
441
441
  'journalAttributes' => a('journalAttributes'),
442
442
  'sortJournalEntries' => a('sortJournalEntries'),
443
- 'costAccount' => a('costAccount'),
444
- 'revenueAccount' => a('revenueAccount') }
443
+ 'costAccount' => a('costaccount'),
444
+ 'revenueAccount' => a('revenueaccount') }
445
445
  query = Query.new(queryAttrs)
446
446
 
447
447
  timeOffId = @report.get('timeOffId')
@@ -18,6 +18,7 @@ require 'taskjuggler/reports/AccountListRE'
18
18
  require 'taskjuggler/reports/TextReport'
19
19
  require 'taskjuggler/reports/TaskListRE'
20
20
  require 'taskjuggler/reports/ResourceListRE'
21
+ require 'taskjuggler/reports/TraceReport'
21
22
  require 'taskjuggler/reports/TagFile'
22
23
  require 'taskjuggler/reports/TjpExportRE'
23
24
  require 'taskjuggler/reports/StatusSheetReport'
@@ -45,6 +46,7 @@ class TaskJuggler
45
46
  # Create a new report object.
46
47
  def initialize(project, id, name, parent)
47
48
  super(project.reports, id, name, parent)
49
+ @messageHandler = MessageHandlerInstance.instance
48
50
  checkFileName(name)
49
51
  project.addReport(self)
50
52
 
@@ -129,6 +131,8 @@ class TaskJuggler
129
131
  @content = TextReport.new(self)
130
132
  when :taskreport
131
133
  @content = TaskListRE.new(self)
134
+ when :tracereport
135
+ @content = TraceReport.new(self)
132
136
  when :statusSheet
133
137
  @content = StatusSheetReport.new(self)
134
138
  when :timeSheet
@@ -154,22 +158,6 @@ class TaskJuggler
154
158
  @project.reportContexts.first.report.get('interactive')
155
159
  end
156
160
 
157
- def error(id, message)
158
- if message && !message.empty?
159
- @project.messageHandler.error(id, message, @sourceFileInfo)
160
- else
161
- # We have no message, so the error has already been reported to the
162
- # MessageHandler. Just trigger another exception to signal the error.
163
- raise TjException
164
- end
165
- end
166
-
167
- def warning(id, message)
168
- if message && !message.empty?
169
- @project.messageHandler.warning(id, message, @sourceFileInfo)
170
- end
171
- end
172
-
173
161
  private
174
162
  # Convenience function to access a report attribute
175
163
  def a(attribute)
@@ -187,7 +175,7 @@ class TaskJuggler
187
175
  return nil
188
176
  end
189
177
 
190
- html = HTMLDocument.new(:strict)
178
+ html = HTMLDocument.new
191
179
  head = html.generateHead(@project['name'] + " - #{get('title') || @name}",
192
180
  'description' => 'TaskJuggler Report',
193
181
  'keywords' => 'taskjuggler, project, management')
@@ -347,7 +335,7 @@ EOT
347
335
  begin
348
336
  f = @name == '.' ? $stdout :
349
337
  File.new(((@name[0] == '/' ? '' : @project.outputDir) +
350
- @name + '.ical').untaint, 'w')
338
+ @name + '.ics').untaint, 'w')
351
339
  f.puts "#{@content.to_iCal}"
352
340
  rescue IOError, SystemCallError
353
341
  error('write_ical', "Cannot write to file #{@name}.\n#{$!}")
@@ -48,25 +48,25 @@ class TaskJuggler
48
48
 
49
49
  # Take the complete account list and remove all accounts that are matching
50
50
  # the hide expression, the rollup Expression or are not a descendent of
51
- # accountRoot.
51
+ # accountroot.
52
52
  def filterAccountList(list_, hideExpr, rollupExpr, openNodes)
53
53
  list = PropertyList.new(list_)
54
- if (accountRoot = a('accountRoot'))
55
- # Remove all accounts that are not descendents of the accountRoot.
56
- list.delete_if { |account| !account.isChildOf?(accountRoot) }
54
+ if (accountroot = a('accountroot'))
55
+ # Remove all accounts that are not descendents of the accountroot.
56
+ list.delete_if { |account| !account.isChildOf?(accountroot) }
57
57
  end
58
58
 
59
59
  standardFilterOps(list, hideExpr, rollupExpr, openNodes, nil,
60
- accountRoot)
60
+ accountroot)
61
61
  end
62
62
 
63
63
  # Take the complete task list and remove all tasks that are matching the
64
64
  # hide expression, the rollup Expression or are not a descendent of
65
- # taskRoot. In case resource is not nil, a task is only included if
65
+ # taskroot. In case resource is not nil, a task is only included if
66
66
  # the resource is allocated to it in any of the reported scenarios.
67
67
  def filterTaskList(list_, resource, hideExpr, rollupExpr, openNodes)
68
68
  list = PropertyList.new(list_)
69
- if (taskRoot = a('taskRoot'))
69
+ if (taskRoot = a('taskroot'))
70
70
  # Remove all tasks that are not descendents of the taskRoot.
71
71
  list.delete_if { |task| !task.isChildOf?(taskRoot) }
72
72
  end
@@ -94,11 +94,11 @@ class TaskJuggler
94
94
 
95
95
  # Take the complete resource list and remove all resources that are matching
96
96
  # the hide expression, the rollup Expression or are not a descendent of
97
- # resourceRoot. In case task is not nil, a resource is only included if
97
+ # resourceroot. In case task is not nil, a resource is only included if
98
98
  # it is assigned to the task in any of the reported scenarios.
99
99
  def filterResourceList(list_, task, hideExpr, rollupExpr, openNodes)
100
100
  list = PropertyList.new(list_)
101
- if (resourceRoot = a('resourceRoot'))
101
+ if (resourceRoot = a('resourceroot'))
102
102
  # Remove all resources that are not descendents of the resourceRoot.
103
103
  list.delete_if { |resource| !resource.isChildOf?(resourceRoot) }
104
104
  end
@@ -38,8 +38,8 @@ class TaskJuggler
38
38
  'journalMode' => @report.get('journalMode'),
39
39
  'journalAttributes' => @report.get('journalAttributes'),
40
40
  'sortJournalEntries' => @report.get('sortJournalEntries'),
41
- 'costAccount' => @report.get('costAccount'),
42
- 'revenueAccount' => @report.get('revenueAccount')
41
+ 'costAccount' => @report.get('costaccount'),
42
+ 'revenueAccount' => @report.get('revenueaccount')
43
43
  }
44
44
  @query = Query.new(queryAttrs)
45
45
  if (@parent = @project.reportContexts.last)
@@ -82,17 +82,17 @@ class TaskJuggler
82
82
  queryAttrs = { 'project' => @project,
83
83
  'scopeProperty' => nil,
84
84
  'scenarioIdx' => scenarioIdx,
85
- 'loadUnit' => a('loadUnit'),
86
- 'numberFormat' => a('numberFormat'),
87
- 'timeFormat' => a('timeFormat'),
85
+ 'loadUnit' => :days,
86
+ 'numberFormat' => RealFormat.new([ '-', '', '', '.', 1]),
87
+ 'timeFormat' => "%Y-%m-%d",
88
88
  'currencyFormat' => a('currencyFormat'),
89
89
  'start' => a('start'), 'end' => a('end'),
90
90
  'hideJournalEntry' => a('hideJournalEntry'),
91
91
  'journalMode' => a('journalMode'),
92
92
  'journalAttributes' => a('journalAttributes'),
93
93
  'sortJournalEntries' => a('sortJournalEntries'),
94
- 'costAccount' => a('costAccount'),
95
- 'revenueAccount' => a('revenueAccount') }
94
+ 'costAccount' => a('costaccount'),
95
+ 'revenueAccount' => a('revenueaccount') }
96
96
  resourceList.query = Query.new(queryAttrs)
97
97
  resourceList.sort!
98
98
 
@@ -129,7 +129,7 @@ class TaskJuggler
129
129
  # all it's sub tasks.
130
130
  entries = @project['journal'].
131
131
  currentEntriesR(a('end'), task, 0, a('start') + 1,
132
- resourceList.query.hideJournalEntry)
132
+ resourceList.query)
133
133
  next if entries.empty?
134
134
 
135
135
  manager.responsibilities << ManagerResponsibilities.new(task, entries)
@@ -29,14 +29,16 @@ class TaskJuggler
29
29
 
30
30
  @@propertiesById = {
31
31
  # ID Header Indent Align Scen Spec.
32
+ 'activetasks' => [ 'Active Tasks', true, :right, true ],
32
33
  'annualleave' => [ 'Annual Leave', true, :right, true ],
33
- 'annualleavebalance'=> [ 'Annual Leave Balance', false, :right, true ],
34
+ 'annualleavebalance'=> [ 'Annual Leave Balance', false, :right, true ],
34
35
  'alert' => [ 'Alert', true, :left, false ],
35
36
  'alertmessages' => [ 'Alert Messages', false, :left, false ],
36
37
  'alertsummaries' => [ 'Alert Summaries', false, :left, false ],
37
38
  'alerttrend' => [ 'Alert Trend', false, :left, false ],
38
39
  'balance' => [ 'Balance', true, :right, true ],
39
40
  'bsi' => [ 'BSI', false, :left, false ],
41
+ 'closedtasks' => [ 'Closed Tasks', true, :right, true ],
40
42
  'complete' => [ 'Completion', false, :right, true ],
41
43
  'cost' => [ 'Cost', true, :right, true ],
42
44
  'duration' => [ 'Duration', true, :right, true ],
@@ -57,6 +59,7 @@ class TaskJuggler
57
59
  'line' => [ 'Line No.', false, :right, false ],
58
60
  'name' => [ 'Name', true, :left, false ],
59
61
  'no' => [ 'No.', false, :right, false ],
62
+ 'opentasks' => [ 'Open Tasks', true, :right, true ],
60
63
  'precursors' => [ 'Precursors', false, :left, true ],
61
64
  'rate' => [ 'Rate', true, :right, true ],
62
65
  'resources' => [ 'Resources', false, :left, true ],
@@ -208,33 +211,37 @@ class TaskJuggler
208
211
  # If we have user specified dates for the report period or the column
209
212
  # period, we don't adjust the period. This flag is used to mark if we
210
213
  # have user-provided values.
211
- doNotAdjust = false
214
+ doNotAdjustStart = false
215
+ doNotAdjustEnd = false
212
216
 
213
217
  # Determine the start date for the column.
214
218
  if columnDef.start
215
219
  # We have a user-specified, column specific start date.
216
220
  rStart = columnDef.start
217
- doNotAdjust = true
221
+ doNotAdjustStart = true
218
222
  else
219
223
  # Use the report start date.
220
224
  rStart = a('start')
221
- doNotAdjust = true if rStart != @project['start']
225
+ doNotAdjustStart = true if rStart != @project['start']
222
226
  end
223
227
 
224
228
  if columnDef.end
225
229
  rEnd = columnDef.end
226
- doNotAdjust = true
230
+ doNotAdjustEnd = true
227
231
  else
228
232
  rEnd = a('end')
229
- doNotAdjust = true if rEnd != @project['end']
233
+ doNotAdjustEnd = true if rEnd != @project['end']
230
234
  end
235
+ origStart = rStart
236
+ origEnd = rEnd
231
237
 
232
238
  # Save the unadjusted dates to the columns Hash.
233
239
  @columns[columnDef] = TableReportColumn.new(rStart, rEnd)
234
240
 
235
241
  # If the task list is empty or the user has provided a custom start or
236
242
  # end date, we don't touch the report period.
237
- return if tasks.empty? || scenarios.empty? || doNotAdjust
243
+ return if tasks.empty? || scenarios.empty? ||
244
+ (doNotAdjustStart && doNotAdjustEnd)
238
245
 
239
246
  # Find the start date of the earliest tasks included in the report and
240
247
  # the end date of the last included tasks.
@@ -289,19 +296,21 @@ class TaskJuggler
289
296
  margin = entry[0] * entry[2]
290
297
  minWidth = entry[0] * entry[1] if minWidth < entry[0] * entry[1]
291
298
  else
292
- doNotAdjust = true
299
+ doNotAdjustStart = doNotAdjustEnd = true
293
300
  end
294
301
 
295
- unless doNotAdjust
302
+ unless doNotAdjustStart && doNotAdjustEnd
296
303
  if minWidth > (rEnd - rStart + 1)
297
- margin += (minWidth - (rEnd - rStart + 1)) / 2
304
+ margin = (minWidth - (rEnd - rStart + 1)) / 2
298
305
  end
299
306
 
300
307
  rStart -= margin
301
308
  rEnd += margin
302
309
 
303
310
  # Save the adjusted dates to the columns Hash.
304
- @columns[columnDef] = TableReportColumn.new(rStart, rEnd)
311
+ @columns[columnDef] = TableReportColumn.new(
312
+ doNotAdjustStart ? origStart : rStart,
313
+ doNotAdjustEnd ? origEnd : rEnd)
305
314
  end
306
315
  end
307
316
 
@@ -390,7 +399,7 @@ class TaskJuggler
390
399
  line.no = no
391
400
  line.lineNo = lineNo
392
401
  line.subLineNo = @table.lines
393
- setIndent(line, a('accountRoot'), accountList.treeMode?)
402
+ setIndent(line, a('accountroot'), accountList.treeMode?)
394
403
 
395
404
  # Generate a cell for each column in this line.
396
405
  a('columns').each do |columnDef|
@@ -437,7 +446,7 @@ class TaskJuggler
437
446
  line.no = no unless scopeLine
438
447
  line.lineNo = lineNo
439
448
  line.subLineNo = @table.lines
440
- setIndent(line, a('taskRoot'), taskList.treeMode?)
449
+ setIndent(line, a('taskroot'), taskList.treeMode?)
441
450
 
442
451
  # Generate a cell for each column in this line.
443
452
  a('columns').each do |columnDef|
@@ -494,7 +503,7 @@ class TaskJuggler
494
503
  line.no = no unless scopeLine
495
504
  line.lineNo = lineNo
496
505
  line.subLineNo = @table.lines
497
- setIndent(line, a('resourceRoot'), resourceList.treeMode?)
506
+ setIndent(line, a('resourceroot'), resourceList.treeMode?)
498
507
 
499
508
  # Generate a cell for each column in this line.
500
509
  a('columns').each do |column|
@@ -953,7 +962,7 @@ class TaskJuggler
953
962
  query.scopeProperty = nil
954
963
  query.attributeId = 'effort'
955
964
  query.startIdx = @project.dateToIdx(t)
956
- query.endIdx = @project.dateToIdx(nextT) - 1
965
+ query.endIdx = @project.dateToIdx(nextT)
957
966
  query.process
958
967
  workLoad = query.to_num
959
968
  scaledWorkLoad = query.to_s
@@ -187,17 +187,17 @@ EOT
187
187
  queryAttrs = { 'project' => @project,
188
188
  'scopeProperty' => nil,
189
189
  'scenarioIdx' => scenarioIdx,
190
- 'loadUnit' => a('loadUnit'),
191
- 'numberFormat' => a('numberFormat'),
192
- 'timeFormat' => a('timeFormat'),
190
+ 'loadUnit' => :days,
191
+ 'numberFormat' => RealFormat.new([ '-', '', '', '.', 1]),
192
+ 'timeFormat' => "%Y-%m-%d",
193
193
  'currencyFormat' => a('currencyFormat'),
194
194
  'start' => from, 'end' => to,
195
195
  'hideJournalEntry' => a('hideJournalEntry'),
196
196
  'journalMode' => a('journalMode'),
197
197
  'journalAttributes' => a('journalAttributes'),
198
198
  'sortJournalEntries' => a('sortJournalEntries'),
199
- 'costAccount' => a('costAccount'),
200
- 'revenueAccount' => a('revenueAccount') }
199
+ 'costAccount' => a('costaccount'),
200
+ 'revenueAccount' => a('revenueaccount') }
201
201
  resourceList.query = Query.new(queryAttrs)
202
202
  resourceList.sort!
203
203
 
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env ruby -w
2
+ # encoding: UTF-8
3
+ #
4
+ # = TraceReport.rb -- The TaskJuggler III Project Management Software
5
+ #
6
+ # Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012
7
+ # by Chris Schlaeger <chris@linux.com>
8
+ #
9
+ # This program is free software; you can redistribute it and/or modify
10
+ # it under the terms of version 2 of the GNU General Public License as
11
+ # published by the Free Software Foundation.
12
+ #
13
+
14
+ require 'taskjuggler/reports/ReportBase'
15
+ require 'taskjuggler/reports/CSVFile'
16
+ require 'taskjuggler/reports/ChartPlotter'
17
+ require 'taskjuggler/TableColumnSorter'
18
+ require 'taskjuggler/MessageHandler'
19
+
20
+ class TaskJuggler
21
+
22
+ # The trace report is used to periodically snapshot a specific list of
23
+ # property attributes and add them to a CSV file.
24
+ class TraceReport < ReportBase
25
+
26
+ include MessageHandler
27
+
28
+ # Create a new object and set some default values.
29
+ def initialize(report)
30
+ super
31
+ @table = nil
32
+ end
33
+
34
+ # Generate the table in the intermediate format.
35
+ def generateIntermediateFormat
36
+ super
37
+
38
+ queryAttrs = { 'project' => @project,
39
+ 'scopeProperty' => nil,
40
+ 'loadUnit' => a('loadUnit'),
41
+ 'numberFormat' => a('numberFormat'),
42
+ # We use a hardcoded %Y-%m-%d format for tracereports.
43
+ 'timeFormat' => "%Y-%m-%d",
44
+ 'currencyFormat' => a('currencyFormat'),
45
+ 'start' => a('start'), 'end' => a('end'),
46
+ 'hideJournalEntry' => a('hideJournalEntry'),
47
+ 'journalMode' => a('journalMode'),
48
+ 'journalAttributes' => a('journalAttributes'),
49
+ 'sortJournalEntries' => a('sortJournalEntries'),
50
+ 'costAccount' => a('costaccount'),
51
+ 'revenueAccount' => a('revenueaccount') }
52
+ query = Query.new(queryAttrs)
53
+
54
+ # Prepare the account list.
55
+ accountList = PropertyList.new(@project.accounts)
56
+ accountList.setSorting(a('sortAccounts'))
57
+ accountList.query = query
58
+ accountList = filterAccountList(accountList, a('hideAccount'),
59
+ a('rollupAccount'), a('openNodes'))
60
+ accountList.sort!
61
+
62
+ # Prepare the resource list.
63
+ resourceList = PropertyList.new(@project.resources)
64
+ resourceList.setSorting(a('sortResources'))
65
+ resourceList.query = query
66
+ resourceList = filterTaskList(resourceList, nil, a('hideResource'),
67
+ a('rollupResource'), a('openNodes'))
68
+ resourceList.sort!
69
+
70
+ # Prepare the task list.
71
+ taskList = PropertyList.new(@project.tasks)
72
+ taskList.includeAdopted
73
+ taskList.setSorting(a('sortTasks'))
74
+ taskList.query = query
75
+ taskList = filterTaskList(taskList, nil, a('hideTask'), a('rollupTask'),
76
+ a('openNodes'))
77
+ taskList.sort!
78
+
79
+ @fileName = ((@report.name[0] == '/' ? '' : @project.outputDir) +
80
+ @report.name + '.csv').untaint
81
+
82
+ # Generate the table header.
83
+ headers = [ 'Date' ] +
84
+ generatePropertyListHeader(accountList, query) +
85
+ generatePropertyListHeader(resourceList, query) +
86
+ generatePropertyListHeader(taskList, query)
87
+
88
+ discontinuedColumns = 0
89
+ if File.exists?(@fileName)
90
+ begin
91
+ @table = CSVFile.new.read(@fileName)
92
+ rescue
93
+ error('tr_cannot_read_csv',
94
+ "Cannot read CSV file #{@fileName}: #{$!}")
95
+ end
96
+
97
+ if @table[0] != headers
98
+ # Some columns have changed. We move all discontinued columns to the
99
+ # last columns and rearrange the others according to the new
100
+ # headers. New columns will be filled with nil in previous rows.
101
+ sorter = TableColumnSorter.new(@table)
102
+ @table = sorter.sort(headers)
103
+ discontinuedColumns = sorter.discontinuedColumns
104
+ end
105
+ else
106
+ @table = [ headers ]
107
+ end
108
+
109
+ # Convert empty strings into nil objects and dates in %Y-%m-%d format
110
+ # into TjTime objects.
111
+ @table.each do |line|
112
+ line.length.times do |i|
113
+ if line[i] == ''
114
+ line[i] = nil
115
+ elsif line[i].is_a?(String) && /\d{4}-\d{2}-\d{2}/ =~ line[i]
116
+ line[i] = TjTime.new(line[i])
117
+ end
118
+ end
119
+ end
120
+
121
+ query = @project.reportContexts.last.query.dup
122
+ dateTag = @project['now'].midnight
123
+
124
+ idx = @table.index { |line| line[0] == dateTag }
125
+ discColumnValues = discontinuedColumns > 0 ?
126
+ Array.new(discontinuedColumns, nil) : []
127
+ if idx
128
+ # We already have an entry for the current date. All old values of
129
+ # this line will be overwritten with the current values. The old
130
+ # values in the discontinued columns will be kept.
131
+ if discontinuedColumns > 0
132
+ discColumnValues = @table[idx][headers.length..-1]
133
+ end
134
+ @table[idx] = []
135
+ else
136
+ # Append a new line of values to the table.
137
+ @table << []
138
+ idx = -1
139
+ end
140
+ # The first entry is always the current date.
141
+ @table[idx] << dateTag
142
+
143
+ # Now add the new values to the line
144
+ generatePropertyListValues(idx, accountList, query)
145
+ generatePropertyListValues(idx, resourceList, query)
146
+ generatePropertyListValues(idx, taskList, query)
147
+
148
+ # Fill the discontinued columns with old values or nil.
149
+ @table[idx] += discColumnValues
150
+
151
+ # Sort the table by ascending first column dates. We need to ensure that
152
+ # the header remains the first line in the table.
153
+ @table.sort! { |l1, l2| l1[0].is_a?(String) ? -1 :
154
+ (l2[0].is_a?(String) ? 1 : l1[0] <=> l2[0]) }
155
+ end
156
+
157
+ def to_html
158
+ html = []
159
+ html << rt_to_html('header')
160
+
161
+ begin
162
+ plotter = ChartPlotter.new(a('width'), a('height'), @table)
163
+ plotter.generate
164
+ html << plotter.to_svg
165
+ rescue ChartPlotterError => exception
166
+ warning('chartPlotterError', exception.message, @report.sourceFileInfo)
167
+ end
168
+
169
+ html << rt_to_html('footer')
170
+
171
+ html
172
+ end
173
+
174
+ def to_csv
175
+ # Convert all TjTime values into String with format %Y-%m-%d and nil
176
+ # objects into empty Strings.
177
+ @table.each do |line|
178
+ line.length.times do |i|
179
+ if line[i].nil?
180
+ line[i] = ''
181
+ elsif line[i].is_a?(TjTime)
182
+ line[i] = line[i].to_s('%Y-%m-%d')
183
+ end
184
+ end
185
+ end
186
+
187
+ @table
188
+ end
189
+
190
+ private
191
+
192
+ def generatePropertyListHeader(propertyList, query)
193
+ headers = []
194
+ query = query.dup
195
+ a('columns').each do |columnDescr|
196
+ query.attributeId = columnDescr.id
197
+ a('scenarios').each do |scenarioIdx|
198
+ query.scenarioIdx = scenarioIdx
199
+ propertyList.each do |property|
200
+ query.property = property
201
+
202
+ #adjustColumnPeriod(columnDescr, propertyList, a.get('scenarios'))
203
+ header = SimpleQueryExpander.new(columnDescr.title, query,
204
+ @report.sourceFileInfo).expand
205
+
206
+ if headers.include?(header)
207
+ error('trace_columns_not_uniq',
208
+ "The column title '#{header}' is already used " +
209
+ "by a previous column. Column titles must be " +
210
+ "unique!")
211
+ end
212
+
213
+ headers << header
214
+ end
215
+ end
216
+ end
217
+ headers
218
+ end
219
+
220
+ def generatePropertyListValues(idx, propertyList, query)
221
+ @report.get('columns').each do |columnDescr|
222
+ query.attributeId = columnDescr.id
223
+
224
+ a('scenarios').each do |scenarioIdx|
225
+ query.scenarioIdx = scenarioIdx
226
+
227
+ propertyList.each do |property|
228
+ query.property = property
229
+
230
+ query.process
231
+ @table[idx] << query.result
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ def columnTitle(property, scenarioIdx, columnDescr)
238
+ title = columnDescr.title.dup
239
+ # The title can be parameterized by including mini-queries for the ID
240
+ # or the name of the property, the scenario id or the attribute ID.
241
+ title.gsub!(/<-id->/, property.fullId)
242
+ title.gsub!(/<-scenario->/, @project.scenario(scenarioIdx).id)
243
+ title.gsub!(/<-name->/, property.name)
244
+ title.gsub!(/<-attribute->/, columnDescr.id)
245
+ title
246
+ end
247
+
248
+ end
249
+
250
+ end
251
+