taskjuggler 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
+