Typica is a free program for professional coffee roasters. https://typica.us
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

chart.xml 23KB

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