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 22KB

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