Typica is a free program for professional coffee roasters. https://typica.us
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

chart.xml 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. <window id="pytdprodcomp">
  2. <reporttitle>Production:->Previous Year Production Comparison</reporttitle>
  3. <layout type="vertical">
  4. <layout type="horizontal">
  5. <label>Batch Type:</label>
  6. <sqldrop id="batchtype" />
  7. <label>Approval:</label>
  8. <sqldrop id="approval" />
  9. <daterange id="dates" initial="19" /><!-- Current Year to Date -->
  10. <label>Days to Average:</label>
  11. <line validator="integer" id="days">7</line>
  12. <label>Weight Unit:</label>
  13. <sqldrop id="unit" />
  14. <stretch />
  15. </layout>
  16. <webview id="report" />
  17. </layout>
  18. <menu name="File">
  19. <item id="print" shortcut="Ctrl+P">Print...</item>
  20. </menu>
  21. <menu name="Reports" type="reports" src="Reports" />
  22. <program>
  23. <![CDATA[
  24. this.windowTitle = TTR("pytdprodcomp", "Typica - Previous Year Production Comparison");
  25. var dateSelect = findChildObject(this, 'dates');
  26. dateSelect.removeIndex(23); // Remove Lifetime range
  27. var view = findChildObject(this, 'report');
  28. var printMenu = findChildObject(this, 'print');
  29. printMenu.triggered.connect(function() {
  30. view.print();
  31. });
  32. var avgField = findChildObject(this, 'days');
  33. var unitBox = findChildObject(this, 'unit');
  34. unitBox.addItem(TTR("pytdprodcomp", "Kg"));
  35. unitBox.addItem(TTR("pytdprodcomp", "Lb"));
  36. unitBox.currentIndex = QSettings.value("script/report_unit", 1);
  37. unitBox['currentIndexChanged(int)'].connect(function() {
  38. QSettings.setValue("script/report_unit", unitBox.currentIndex);
  39. refresh();
  40. });
  41. var batchType = findChildObject(this, 'batchtype');
  42. batchType.addItem(TTR("pytdprodcomp", "Any"));
  43. batchType.addItem(TTR("pytdprodcomp", "Production Roasts"));
  44. batchType.addItem(TTR("pytdprodcomp", "Sample Roasts"));
  45. batchType.currentIndex = QSettings.value("script/batchtypefilter", 1);
  46. batchType['currentIndexChanged(int)'].connect(function() {
  47. QSettings.setValue("script/batchtypefilter", batchType.currentIndex);
  48. refresh();
  49. });
  50. var approval = findChildObject(this, 'approval');
  51. approval.addItem(TTR("pytdprodcomp", "Any"));
  52. approval.addItem(TTR("pytdprodcomp", "Approved"));
  53. approval.addItem(TTR("pytdprodcomp", "Not Approved"));
  54. approval.currentIndex = QSettings.value("script/approvalfilter", 1);
  55. approval['currentIndexChanged(int)'].connect(function() {
  56. QSettings.setValue("script/approvalfilter", approval.currentIndex);
  57. refresh();
  58. });
  59. function refresh() {
  60. var conversion = 1;
  61. if(unitBox.currentIndex == 0) {
  62. conversion = 2.2;
  63. }
  64. var buffer = new QBuffer;
  65. buffer.open(3);
  66. var output = new XmlWriter(buffer);
  67. output.writeStartDocument("1.0");
  68. output.writeDTD('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg.dtd">');
  69. output.writeStartElement("html");
  70. output.writeAttribute("xmlns", "http://www.w3.org/1999/xhtml");
  71. output.writeStartElement("head");
  72. output.writeTextElement("title", TTR("pytdprodcomp", "Previous Year Production Comparison"));
  73. output.writeEndElement();
  74. output.writeStartElement("body");
  75. var cdt = new Date(Date.now());
  76. output.writeTextElement("p", cdt.toLocaleDateString(TTR("reports", "en-US")) + " " + cdt.toLocaleTimeString(TTR("reports", "en-US")));
  77. output.writeTextElement("h1", TTR("pytdprodcomp", "Previous Year Production Comparison"));
  78. output.writeTextElement("p", TTR("pytdprodcomp", "This report provides an itemized and overall comparison of roasted coffee production for the dates specified with those dates in the previous year. A chart of this data along with percent change and rolling average of the percent change is also produced."));
  79. output.writeStartElement("table");
  80. output.writeAttribute("style", "page-break-after:auto;");
  81. output.writeAttribute("rules", "groups");
  82. output.writeAttribute("cellpadding", "3px");
  83. output.writeStartElement("thead");
  84. output.writeStartElement("tr");
  85. output.writeTextElement("th", "Coffee");
  86. switch(unitBox.currentIndex) {
  87. case 0:
  88. output.writeTextElement("th", TTR("pytdprodcomp", "Previous (Kg)"));
  89. output.writeTextElement("th", TTR("pytdprodcomp", "Current (Kg)"));
  90. output.writeTextElement("th", TTR("pytdprodcomp", "Change (Kg)"));
  91. break;
  92. case 1:
  93. output.writeTextElement("th", TTR("pytdprodcomp", "Previous (Lb)"));
  94. output.writeTextElement("th", TTR("pytdprodcomp", "Current (Lb)"));
  95. output.writeTextElement("th", TTR("pytdprodcomp", "Change (Lb)"));
  96. break;
  97. }
  98. output.writeEndElement();
  99. output.writeEndElement();
  100. output.writeStartElement("tbody");
  101. var query = new QSqlQuery();
  102. query.exec("START TRANSACTION");
  103. var dateRange = dateSelect.currentRange();
  104. var curStartDate = "'"+dateRange[0]+"'";
  105. var curEndDate = "'"+dateRange[dateRange.length - 1]+"'";
  106. query.exec("SELECT "+curStartDate+"::date - interval '1 year', "+curEndDate+"::date - interval '1 year' + interval '1 day', "+curEndDate+"::date + interval '1 day'");
  107. query.next();
  108. curEndDate = "'"+query.value(2)+"'";
  109. var prevStartDate = "'"+query.value(0)+"'";
  110. var prevEndDate = "'"+query.value(1)+"'";
  111. var transaction_filter;
  112. var approval_filter;
  113. switch(batchType.currentIndex) {
  114. case 0:
  115. transaction_filter = "";
  116. break;
  117. case 1:
  118. transaction_filter = " AND transaction_type = 'ROAST'";
  119. break;
  120. case 2:
  121. transaction_filter = " AND transaction_type = 'SAMPLEROAST'";
  122. break;
  123. }
  124. switch(approval.currentIndex) {
  125. case 0:
  126. approval_filter = "";
  127. break;
  128. case 1:
  129. approval_filter = " AND approval = true";
  130. break;
  131. case 2:
  132. approval_filter = " AND approval = false";
  133. break;
  134. }
  135. var q = "CREATE TEMPORARY TABLE previous ON COMMIT DROP AS SELECT roasted_id, sum(roasted_quantity) AS p FROM roasting_log WHERE time > "+prevStartDate+" AND time < "+prevEndDate+" AND roasted_id IS NOT NULL" + transaction_filter + approval_filter + " GROUP BY roasted_id";
  136. query.exec(q);
  137. q = "CREATE TEMPORARY TABLE current ON COMMIT DROP AS SELECT roasted_id, sum(roasted_quantity) AS c FROM roasting_log WHERE time > "+curStartDate+" AND time < "+curEndDate+" AND roasted_id IS NOT NULL" + transaction_filter + approval_filter + " GROUP BY roasted_id";
  138. query.exec(q);
  139. query.exec("INSERT INTO previous SELECT roasted_id, 0 FROM current WHERE roasted_id NOT IN (SELECT roasted_id FROM previous)");
  140. query.exec("INSERT INTO current SELECT roasted_id, 0 FROM previous WHERE roasted_id NOT IN (SELECT roasted_id FROM current)");
  141. query.exec("CREATE TEMPORARY TABLE comp ON COMMIT DROP AS SELECT previous.roasted_id, p, c FROM previous LEFT OUTER JOIN current ON previous.roasted_id = current.roasted_id");
  142. query.exec("SELECT (SELECT name FROM items WHERE id = roasted_id) AS name, p, c, (c-p) FROM comp WHERE p > 0 OR c > 0 ORDER BY name");
  143. while(query.next())
  144. {
  145. output.writeStartElement("tr");
  146. output.writeTextElement("td", query.value(0));
  147. output.writeTextElement("td", (query.value(1) / conversion).toFixed(2));
  148. output.writeTextElement("td", (query.value(2) / conversion).toFixed(2));
  149. output.writeTextElement("td", (query.value(3) / conversion).toFixed(2));
  150. output.writeEndElement();
  151. }
  152. output.writeEndElement();
  153. output.writeStartElement("tfoot");
  154. output.writeTextElement("th", TTR("pytdprodcomp", "Totals"));
  155. query.exec("SELECT sum(p), sum(c), sum(c-p) FROM comp");
  156. query.next();
  157. output.writeTextElement("td", (query.value(0) / conversion).toFixed(2));
  158. output.writeTextElement("td", (query.value(1) / conversion).toFixed(2));
  159. output.writeTextElement("td", (query.value(2) / conversion).toFixed(2));
  160. output.writeEndElement();
  161. query.exec("ABORT");
  162. output.writeEndElement();
  163. output.writeStartElement("p");
  164. output.writeAttribute("style", "page-break-inside: avoid");
  165. output.writeStartElement("svg");
  166. output.writeAttribute("xmlns", "http://www.w3.org/2000/svg");
  167. output.writeAttribute("width", "7.5in");
  168. output.writeAttribute("height", "6.3in");
  169. output.writeStartElement("g");
  170. output.writeAttribute("transform", "translate(0,470)");
  171. output.writeStartElement("g");
  172. output.writeAttribute("transform", "scale(1,-1)");
  173. output.writeStartElement("line");
  174. output.writeAttribute("x1", "40");
  175. output.writeAttribute("x2", "40");
  176. output.writeAttribute("y1", "0");
  177. output.writeAttribute("y2", "450");
  178. output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
  179. output.writeEndElement();
  180. output.writeStartElement("line");
  181. output.writeAttribute("x1", "50");
  182. output.writeAttribute("x2", "650");
  183. output.writeAttribute("y1", "-10");
  184. output.writeAttribute("y2", "-10");
  185. output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
  186. output.writeEndElement();
  187. output.writeStartElement("line");
  188. output.writeAttribute("x1", "660");
  189. output.writeAttribute("x2", "660");
  190. output.writeAttribute("y1", "0");
  191. output.writeAttribute("y2", "450");
  192. output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
  193. output.writeEndElement();
  194. var q = "SELECT "+curEndDate+"::date - "+curStartDate+"::date";
  195. query.exec(q);
  196. query.next();
  197. var days = query.value(0);
  198. var dates = new Array();
  199. var curpounds = new Array();
  200. var prevpounds = new Array();
  201. var change = new Array();
  202. var avgchange = new Array();
  203. var i;
  204. for(i = 0; i < days; i++)
  205. {
  206. q = "SELECT "+curStartDate+"::date + "+(i+1);
  207. query.exec(q);
  208. query.next();
  209. dates[i] = query.value(0);
  210. q = "SELECT sum(roasted_quantity) FROM roasting_log WHERE time > "+curStartDate+" AND time < '"+dates[i]+"'" + transaction_filter + approval_filter;
  211. query.exec(q);
  212. if(query.next())
  213. {
  214. curpounds[i] = Number(query.value(0));
  215. }
  216. else
  217. {
  218. curpounds[i] = 0;
  219. }
  220. q = "SELECT sum(roasted_quantity) FROM roasting_log WHERE time > "+curStartDate+"::date - '1 year'::interval AND time < '"+dates[i]+"'::date - '1 year'::interval" + transaction_filter + approval_filter;
  221. query.exec(q);
  222. if(query.next())
  223. {
  224. prevpounds[i] = Number(query.value(0));
  225. }
  226. else
  227. {
  228. prevpounds[i] = 0;
  229. }
  230. if(curpounds[i] > 0)
  231. {
  232. change[i] = (curpounds[i] - prevpounds[i])/curpounds[i];
  233. }
  234. else
  235. {
  236. if(prevpounds[i] > 0)
  237. {
  238. change[i] = -1;
  239. }
  240. else
  241. {
  242. change[i] = 0;
  243. }
  244. }
  245. if(i > (parseInt(avgField.text)-2))
  246. {
  247. var sum = 0;
  248. var j;
  249. for(j = 0; j < parseInt(avgField.text); j++)
  250. {
  251. sum += change[i-j];
  252. }
  253. avgchange[i] = sum / parseInt(avgField.text);
  254. }
  255. }
  256. // Calculate the domain of the primary y axis.
  257. var maxy1 = 0;
  258. var increment = 100; // Starting increment is 100 Lb.
  259. if(unitBox.currentIndex == 0) {
  260. increment = 110; // Starting increment is 50 Kg if Kg is requested.
  261. }
  262. while(maxy1 < Math.max(curpounds[days - 1], prevpounds[days - 1]))
  263. {
  264. maxy1 += increment;
  265. }
  266. // Calculate the number of grid lines and loosen spacing if there are too many.
  267. var divisions = maxy1 / increment;
  268. while(divisions > 10) {
  269. increment *= 2;
  270. divisions = maxy1 / increment;
  271. }
  272. // Draw y axis grid lines.
  273. var pos = 0;
  274. while(pos <= maxy1)
  275. {
  276. output.writeStartElement("line");
  277. output.writeAttribute("x1", "35");
  278. output.writeAttribute("x2", "45");
  279. output.writeAttribute("y1", (450/maxy1)*pos);
  280. output.writeAttribute("y2", (450/maxy1)*pos);
  281. output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
  282. output.writeEndElement();
  283. pos += increment;
  284. }
  285. var n = Math.min.apply(Math, change);
  286. var m = Math.max.apply(Math, change);
  287. var miny2 = 0;
  288. var maxy2;
  289. if(n < 0)
  290. {
  291. while(miny2 > n)
  292. {
  293. miny2 -= 0.1;
  294. }
  295. maxy2 = miny2;
  296. }
  297. else
  298. {
  299. while(miny2 < n)
  300. {
  301. miny2 += 0.1;
  302. }
  303. miny2 -= 0.1;
  304. maxy2 = miny2;
  305. }
  306. while(maxy2 < m)
  307. {
  308. maxy2 += 0.1;
  309. }
  310. var range = maxy2 - miny2;
  311. pos = miny2;
  312. while(pos <= maxy2)
  313. {
  314. output.writeStartElement("line");
  315. output.writeAttribute("x1", "655");
  316. output.writeAttribute("x2", "665");
  317. output.writeAttribute("y1", (pos-miny2)*(4.5/(range*100))*10000);
  318. output.writeAttribute("y2", (pos-miny2)*(4.5/(range*100))*10000);
  319. output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
  320. output.writeEndElement();
  321. pos += 0.1;
  322. }
  323. pos = 0;
  324. while(pos < days)
  325. {
  326. output.writeStartElement("line");
  327. output.writeAttribute("x1", 50+((600/(days-1))*pos));
  328. output.writeAttribute("x2", 50+((600/(days-1))*pos));
  329. output.writeAttribute("y1", "-5");
  330. output.writeAttribute("y2", "-15");
  331. output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
  332. output.writeEndElement();
  333. pos += 7;
  334. }
  335. output.writeStartElement("line");
  336. output.writeAttribute("x1", 50+((600/(days-1))*(days-1)));
  337. output.writeAttribute("x2", 50+((600/(days-1))*(days-1)));
  338. output.writeAttribute("y1", "-5");
  339. output.writeAttribute("y2", "-15");
  340. output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
  341. output.writeEndElement();
  342. pos = 1;
  343. while(pos < days)
  344. {
  345. output.writeStartElement("line");
  346. output.writeAttribute("x1", 50+((600/(days-1))*(pos - 1)));
  347. output.writeAttribute("x2", 50+((600/(days-1))*pos));
  348. output.writeAttribute("y1", (prevpounds[pos-1]/maxy1)*450);
  349. output.writeAttribute("y2", (prevpounds[pos]/maxy1)*450);
  350. output.writeAttribute("style", "stroke:rgb(0,0,0);stroke-width:1;");
  351. output.writeEndElement();
  352. output.writeStartElement("line");
  353. output.writeAttribute("x1", 50+((600/(days-1))*(pos - 1)));
  354. output.writeAttribute("x2", 50+((600/(days-1))*pos));
  355. output.writeAttribute("y1", (curpounds[pos-1]/maxy1)*450);
  356. output.writeAttribute("y2", (curpounds[pos]/maxy1)*450);
  357. output.writeAttribute("style", "stroke:rgb(255,0,0);stroke-width:1;");
  358. output.writeEndElement();
  359. output.writeStartElement("line");
  360. output.writeAttribute("x1", 50+((600/(days-1))*(pos-1)));
  361. output.writeAttribute("x2", 50+((600/(days-1))*pos));
  362. output.writeAttribute("y1", (change[pos-1]-miny2)*(4.5/(range*100))*10000);
  363. output.writeAttribute("y2", (change[pos]-miny2)*(4.5/(range*100))*10000);
  364. output.writeAttribute("style", "stroke:rgb(0,255,0);stroke-width:1;");
  365. output.writeEndElement();
  366. if(pos > (parseInt(avgField.text)-1))
  367. {
  368. output.writeStartElement("line");
  369. output.writeAttribute("x1", 50+((600/(days-1))*(pos-1)));
  370. output.writeAttribute("x2", 50+((600/(days-1))*pos));
  371. output.writeAttribute("y1", (avgchange[pos-1]-miny2)*(4.5/(range*100))*10000);
  372. output.writeAttribute("y2", (avgchange[pos]-miny2)*(4.5/(range*100))*10000);
  373. output.writeAttribute("style", "stroke:rgb(0,0,255);stroke-width:1;");
  374. output.writeEndElement();
  375. }
  376. pos++;
  377. }
  378. output.writeEndElement();
  379. i = 0;
  380. while(i <= maxy1)
  381. {
  382. output.writeStartElement("text");
  383. output.writeAttribute("x", "0");
  384. output.writeAttribute("y", -((450/maxy1)*i)+5);
  385. output.writeAttribute("font-size", "12");
  386. switch(unitBox.currentIndex) {
  387. case 0:
  388. output.writeCharacters((i / 2.2).toFixed(0));
  389. break;
  390. case 1:
  391. default:
  392. output.writeCharacters(i);
  393. break;
  394. }
  395. output.writeEndElement();
  396. i += increment;
  397. }
  398. i = miny2;
  399. var stepSize = 0.1;
  400. while((maxy2 - miny2) / stepSize > 20)
  401. {
  402. stepSize += 0.05;
  403. }
  404. while(i <= maxy2)
  405. {
  406. output.writeStartElement("text");
  407. output.writeAttribute("x", "670");
  408. output.writeAttribute("y", -((i-miny2)*(4.5/(range*100))*10000)+5);
  409. output.writeAttribute("font-size", "12");
  410. output.writeCharacters(Number(i*100).toFixed(0)+"%");
  411. output.writeEndElement();
  412. i += stepSize;
  413. }
  414. i = 0;
  415. while(i <= days-1)
  416. {
  417. output.writeStartElement("text");
  418. output.writeAttribute("x", 45+((600/(days-1))*i));
  419. output.writeAttribute("y", "20");
  420. output.writeAttribute("font-size", "12");
  421. output.writeAttribute("transform", "rotate(90 "+(45+((600/(days-1))*i))+",20)");
  422. q = "SELECT ('"+dates[i]+"'::date - '1 day'::interval)::date";
  423. query.exec(q);
  424. query.next();
  425. var ds = query.value(0);
  426. var parts = ds.split("-");
  427. switch(parts[1])
  428. {
  429. case '01':
  430. ds = TTR("pytdprodcomp", "January ");
  431. break;
  432. case '02':
  433. ds = TTR("pytdprodcomp", "February ");
  434. break;
  435. case '03':
  436. ds = TTR("pytdprodcomp", "March ");
  437. break;
  438. case '04':
  439. ds = TTR("pytdprodcomp", "April ");
  440. break;
  441. case '05':
  442. ds = TTR("pytdprodcomp", "May ");
  443. break;
  444. case '06':
  445. ds = TTR("pytdprodcomp", "June ");
  446. break;
  447. case '07':
  448. ds = TTR("pytdprodcomp", "July ");
  449. break;
  450. case '08':
  451. ds = TTR("pytdprodcomp", "August ");
  452. break;
  453. case '09':
  454. ds = TTR("pytdprodcomp", "September ");
  455. break;
  456. case '10':
  457. ds = TTR("pytdprodcomp", "October ");
  458. break;
  459. case '11':
  460. ds = TTR("pytdprodcomp", "November ");
  461. break;
  462. case '12':
  463. ds = TTR("pytdprodcomp", "December ");
  464. break;
  465. }
  466. ds = ds + Number(parts[2]);
  467. output.writeCharacters(ds);
  468. output.writeEndElement();
  469. if(i == days-1)
  470. {
  471. break;
  472. }
  473. i += 7;
  474. if(i > days-1)
  475. {
  476. i = days - 1;
  477. }
  478. }
  479. query = query.invalidate();
  480. output.writeStartElement("rect");
  481. output.writeAttribute("fill", "rgb(0,0,0)");
  482. output.writeAttribute("x", "45");
  483. output.writeAttribute("y", "110");
  484. output.writeAttribute("width", "24");
  485. output.writeAttribute("height", "12");
  486. output.writeEndElement();
  487. output.writeStartElement("text");
  488. output.writeAttribute("x", "75");
  489. output.writeAttribute("y", "120");
  490. output.writeAttribute("font-size", "12");
  491. switch(unitBox.currentIndex) {
  492. case 0:
  493. output.writeCharacters(TTR("pytdprodcomp", "Previous Year Kg"));
  494. break;
  495. case 1:
  496. output.writeCharacters(TTR("pytdprodcomp", "Previous Year Lb"));
  497. break;
  498. }
  499. output.writeEndElement();
  500. output.writeStartElement("rect");
  501. output.writeAttribute("fill", "rgb(255,0,0)");
  502. output.writeAttribute("x", "195");
  503. output.writeAttribute("y", "110");
  504. output.writeAttribute("width", "24");
  505. output.writeAttribute("height", "12");
  506. output.writeEndElement();
  507. output.writeStartElement("text");
  508. output.writeAttribute("x", "225");
  509. output.writeAttribute("y", "120");
  510. output.writeAttribute("font-size", "12");
  511. switch(unitBox.currentIndex) {
  512. case 0:
  513. output.writeCharacters(TTR("pytdprodcomp", "Current Year Kg"));
  514. break;
  515. case 1:
  516. output.writeCharacters(TTR("pytdprodcomp", "Current Year Lb"));
  517. break;
  518. }
  519. output.writeEndElement();
  520. output.writeStartElement("rect");
  521. output.writeAttribute("fill", "rgb(0,255,0)");
  522. output.writeAttribute("x", "345");
  523. output.writeAttribute("y", "110");
  524. output.writeAttribute("width", "24");
  525. output.writeAttribute("height", "12");
  526. output.writeEndElement();
  527. output.writeStartElement("text");
  528. output.writeAttribute("x", "375");
  529. output.writeAttribute("y", "120");
  530. output.writeAttribute("font-size", "12");
  531. output.writeCharacters(TTR("pytdprodcomp", "% Change"));
  532. output.writeEndElement();
  533. output.writeStartElement("rect");
  534. output.writeAttribute("fill", "rgb(0,0,255)");
  535. output.writeAttribute("x", "495");
  536. output.writeAttribute("y", "110");
  537. output.writeAttribute("width", "24");
  538. output.writeAttribute("height", "12");
  539. output.writeEndElement();
  540. output.writeStartElement("text");
  541. output.writeAttribute("x", "525");
  542. output.writeAttribute("y", "120");
  543. output.writeAttribute("font-size", "12");
  544. output.writeCharacters(TTR("pytdprodcomp", "Average % Change"));
  545. output.writeEndElement();
  546. output.writeEndElement();
  547. output.writeEndElement();
  548. output.writeEndElement();
  549. output.writeEndElement();
  550. output.writeEndElement();
  551. output.writeEndDocument();
  552. view.setContent(buffer);
  553. buffer.close();
  554. }
  555. refresh();
  556. dateSelect.rangeUpdated.connect(function() {
  557. refresh();
  558. });
  559. avgField.editingFinished.connect(function() {
  560. refresh();
  561. });
  562. var notifier = Application.subscribe("roastinglogchange");
  563. notifier.notify.connect(function() {
  564. refresh();
  565. });
  566. ]]>
  567. </program>
  568. </window>