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.

newbatch.xml 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. <window id="batchWindow">
  2. <menu name="Batch">
  3. <item id="new" shortcut="Ctrl+N">New Batch…</item>
  4. </menu>
  5. <layout type="horizontal">
  6. <layout type="vertical">
  7. <layout type="horizontal">
  8. <label>Machine:</label>
  9. <line id="machine" writable="false" />
  10. <label>Unit:</label>
  11. <sqldrop id="unit" />
  12. <stretch />
  13. </layout>
  14. <layout type="horizontal">
  15. <label>Roasted Coffee:</label>
  16. <sqldrop data="0" display="1" showdata="true" id="roasted">
  17. <null />
  18. <query>SELECT id, name FROM items WHERE category = 'Coffee: Roasted' AND id IN (SELECT item FROM current_items) ORDER BY name</query>
  19. </sqldrop>
  20. <stretch />
  21. </layout>
  22. <label>Green Coffee:</label>
  23. <sqltablearray columns="2" id="greens">
  24. <column name="Coffee" delegate="sql" showdata="true" null="false" data="0" display="1">SELECT id, name FROM coffees WHERE quantity &lt;&gt; 0 ORDER BY name</column>
  25. <column name="Weight" />
  26. </sqltablearray>
  27. <layout type="horizontal">
  28. <label>Green Weight:</label>
  29. <line id="green" writable="false">0.0</line>
  30. </layout>
  31. <layout type="horizontal">
  32. <button name="Load Profile" type="push" id="load" />
  33. <button name="No Profile" type="push" id="noprofile" />
  34. </layout>
  35. <layout type="horizontal">
  36. <label>Time:</label>
  37. <line id="time" writable="false" />
  38. <label>Duration:</label>
  39. <line id="duration" writable="false" />
  40. </layout>
  41. <layout type="horizontal">
  42. <label>Roasted Weight:</label>
  43. <line id="roast" validator="numeric" />
  44. <label>Weight Loss:</label>
  45. <line id="wloss" writable="false" />
  46. <button type="check" id="approval" name="Approved" />
  47. </layout>
  48. <layout type="horizontal">
  49. <label>Annotation:</label>
  50. <textarea id="annotation" />
  51. </layout>
  52. <layout type="horizontal">
  53. <button name="Submit" id="submit" type="push" />
  54. <button name="Save log as target profile" type="check" id="target" />
  55. </layout>
  56. </layout>
  57. <layout type="vertical">
  58. <layout type="vertical" id="scales" />
  59. <stretch />
  60. </layout>
  61. </layout>
  62. <program>
  63. <![CDATA[
  64. var unitBox = findChildObject(this, 'unit');
  65. unitBox.addItem("g");
  66. unitBox.addItem("Kg");
  67. unitBox.addItem("oz");
  68. unitBox.addItem("lb");
  69. unitBox.currentIndex = (QSettings.value("script/batch_unit", unitBox.findText("lb")));
  70. var machine = findChildObject(this, "machine");
  71. machine.setText(selectedRoasterName + " (" + selectedRoasterID + ")");
  72. var newMenu = findChildObject(this, 'new');
  73. newMenu.triggered.connect(function() {
  74. var bwindow = createWindow("batchWindow");
  75. bwindow.windowTitle = "Typica - [*]New Batch";
  76. });
  77. var batch = this;
  78. var table = findChildObject(this, 'greens');
  79. var green = findChildObject(this, 'green');
  80. var model = table.model();
  81. var lossField = findChildObject(this, 'wloss');
  82. lossField.maximumWidth = 80;
  83. var roasted = findChildObject(this, 'roasted');
  84. var roastwt = findChildObject(this, 'roast');
  85. roastwt.maximumWidth = 80;
  86. var scalesLayout = findChildObject(this, 'scales');
  87. scalesLayout.spacing = 10;
  88. if(navigationwindow.loggingWindow.scales.length > 0) {
  89. for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++) {
  90. var scale = navigationwindow.loggingWindow.scales[i];
  91. var label = new QLabel(" ");
  92. var weighButton = new QPushButton();
  93. weighButton.text = "Weigh";
  94. weighButton.clicked.connect(scale.weigh);
  95. label.updateMeasurement = function(m, u) {
  96. this.text = Units.convertWeight(m, u, Units.Pound) + " lb";
  97. };
  98. scalesLayout.addWidget(label);
  99. scalesLayout.addWidget(weighButton);
  100. scale.newMeasurement.connect(function(m, u) {
  101. label.updateMeasurement(m, u);
  102. });
  103. scale.weigh();
  104. }
  105. }
  106. model.dataChanged.connect(function() {
  107. green.text = table.columnSum(1, 0);
  108. table.resizeColumnToContents(0);
  109. if(parseFloat(green.text) > 0)
  110. {
  111. if(parseFloat(roastwt.text) > 0)
  112. {
  113. lossField.text = (((parseFloat(green.text) - parseFloat(roastwt.text)) / parseFloat(green.text)) * 100).toFixed(2) + "%";
  114. }
  115. else
  116. {
  117. lossField.text = "100%";
  118. }
  119. }
  120. });
  121. roastwt.textChanged.connect(function() {
  122. if(parseFloat(green.text) > 0)
  123. {
  124. if(parseFloat(roastwt.text) > 0)
  125. {
  126. lossField.text = (((parseFloat(green.text) - parseFloat(roastwt.text)) / parseFloat(green.text)) * 100).toFixed(2) + "%";
  127. }
  128. else
  129. {
  130. lossField.text = "100%";
  131. }
  132. }
  133. });
  134. var convertToPounds = function(w, u) {
  135. switch(u) {
  136. case "g":
  137. return w * 0.0022;
  138. case "oz":
  139. return w * 0.0625;
  140. case "Kg":
  141. return w * 2.2;
  142. }
  143. return w;
  144. };
  145. var profilebutton = findChildObject(this, 'load');
  146. profilebutton.setEnabled(false);
  147. roasted['currentIndexChanged(int)'].connect(function() {
  148. var query = new QSqlQuery();
  149. var q = "SELECT EXISTS(SELECT 1 FROM item_files WHERE item = ";
  150. q = q + roasted.currentData();
  151. q = q + ")";
  152. query.exec(q);
  153. if(query.next())
  154. {
  155. if(query.value(0) == 'false')
  156. {
  157. profilebutton.setEnabled(false);
  158. }
  159. else
  160. {
  161. profilebutton.setEnabled(true);
  162. }
  163. }
  164. else
  165. {
  166. profilebutton.setEnabled(false);
  167. }
  168. var title = "Typica - [*]New Batch (";
  169. title = title + roasted.currentText;
  170. title = title + ")";
  171. batch.windowTitle = title;
  172. q = "SELECT unroasted_id FROM roasting_log WHERE roasted_id = ";
  173. q = q + roasted.currentData();
  174. q = q + " AND time = (SELECT max(time) FROM roasting_log WHERE roasted_id = ";
  175. q = q + roasted.currentData();
  176. q = q + ")";
  177. query.exec(q);
  178. if(query.next())
  179. {
  180. var unroasted_items = sqlToArray(query.value(0));
  181. var names = [];
  182. q = "SELECT name FROM items WHERE id = :id AND quantity <> 0";
  183. query.prepare(q);
  184. var allInStock = true;
  185. for(var i = 0; i < unroasted_items.length; i++)
  186. {
  187. query.bind("id", unroasted_items[i]);
  188. query.exec();
  189. if(query.next())
  190. {
  191. names[i] = query.value(0);
  192. }
  193. else
  194. {
  195. allInStock = false;
  196. }
  197. }
  198. if(allInStock)
  199. {
  200. for(var i = 0; i < unroasted_items.length; i++)
  201. {
  202. table.setData(i, 0, names[i], 0);
  203. table.setData(i, 0, unroasted_items[i], 32);
  204. }
  205. }
  206. }
  207. });
  208. profilebutton.clicked.connect(function() {
  209. batch.windowModified = true;
  210. currentBatchInfo = batch;
  211. query = new QSqlQuery();
  212. var q = "SELECT files FROM item_files WHERE item = :item AND time = (SELECT max(time) FROM item_files WHERE item = :again)";
  213. query.prepare(q);
  214. query.bind(":item", Number(roasted.currentData()));
  215. query.bind(":again", Number(roasted.currentData()));
  216. query.exec();
  217. if(query.next())
  218. {
  219. var files = query.value(0);
  220. files = files.replace("{", "(");
  221. files = files.replace("}", ")");
  222. q = "SELECT file, name FROM files WHERE id IN ";
  223. q = q + files;
  224. q = q + " AND type = 'profile'";
  225. query.exec(q);
  226. if(query.next())
  227. {
  228. var targetseries = -1;
  229. var buffer = new QBuffer(query.value(0));
  230. var pname = query.value(1);
  231. var input = new XMLInput(buffer, 1);
  232. var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
  233. var log = findChildObject(navigationwindow.loggingWindow, 'log');
  234. log.clear();
  235. graph.clear();
  236. input.newTemperatureColumn.connect(log.setHeaderData);
  237. input.newTemperatureColumn.connect(function(col, text) {
  238. if(text == navigationwindow.loggingWindow.targetcolumnname)
  239. {
  240. targetseries = col;
  241. }
  242. });
  243. input.newAnnotationColumn.connect(log.setHeaderData);
  244. input.measure.connect(graph.newMeasurement);
  245. input.measure.connect(log.newMeasurement);
  246. input.measure.connect(function(data, series) {
  247. if(series == targetseries)
  248. {
  249. targetDetector.newMeasurement(data);
  250. }
  251. });
  252. input.annotation.connect(log.newAnnotation);
  253. var lc;
  254. input.lastColumn.connect(function(c) {
  255. lc = c;
  256. QSettings.setValue("liveColumn", c + 1);
  257. navigationwindow.loggingWindow.postLoadColumnSetup(c)
  258. });
  259. }
  260. }
  261. query = query.invalidate();
  262. navigationwindow.loggingWindow.windowTitle = "Typica - [*]" + pname;
  263. navigationwindow.loggingWindow.raise();
  264. navigationwindow.loggingWindow.activateWindow();
  265. input.input();
  266. log.newAnnotation("End", 1, lc);
  267. });
  268. var noprofilebutton = findChildObject(this, 'noprofile');
  269. noprofilebutton.clicked.connect(function() {
  270. batch.windowModified = true;
  271. currentBatchInfo = batch;
  272. navigationwindow.loggingWindow.raise();
  273. navigationwindow.loggingWindow.activateWindow();
  274. });
  275. var submitbutton = findChildObject(this, 'submit');
  276. var timefield = findChildObject(this, 'time');
  277. var notes = findChildObject(this, 'annotation');
  278. var duration = findChildObject(this, 'duration');
  279. var approval = findChildObject(this, 'approval');
  280. var target = findChildObject(this, 'target');
  281. submitbutton.clicked.connect(function() {
  282. checkQuery = new QSqlQuery();
  283. checkQuery.exec("SELECT 1 FROM machine WHERE id = " + selectedRoasterID);
  284. if(!checkQuery.next())
  285. {
  286. checkQuery.prepare("INSERT INTO machine VALUES(:id, :name)");
  287. checkQuery.bind(":id", selectedRoasterID);
  288. checkQuery.bind(":name", selectedRoasterName);
  289. checkQuery.exec();
  290. }
  291. checkQuery = checkQuery.invalidate();
  292. var q = "INSERT INTO files VALUES(default, :name, 'profile', NULL, :data) RETURNING id";
  293. query = new QSqlQuery();
  294. query.prepare(q);
  295. query.bind(":name", timefield.text + " " + roasted.currentText);
  296. query.bindFileData(":data", batch.tempData);
  297. query.exec();
  298. query.next();
  299. var fileno = query.value(0);
  300. var file = new QFile(batch.tempData);
  301. file.remove();
  302. var q2 = "INSERT INTO roasting_log VALUES(:time, ";
  303. q2 = q2 + table.columnArray(0, 32);
  304. q2 = q2 + ", ";
  305. for(var i = 0; table.data(i, 1, 0).value != ""; i++)
  306. {
  307. table.setData(i, 1, convertToPounds(parseFloat(table.data(i, 1, 0)), unitBox.currentText) ,32)
  308. }
  309. q2 = q2 + table.columnArray(1, 32);
  310. q2 = q2 + ", ";
  311. q2 = q2 + convertToPounds(parseFloat(green.text), unitBox.currentText);
  312. q2 = q2 + ", ";
  313. q2 = q2 + roasted.currentData();
  314. q2 = q2 + ", ";
  315. q2 = q2 + convertToPounds(parseFloat(roastwt.text), unitBox.currentText);
  316. q2 = q2 + ", 'ROAST', :annotation, ";
  317. q2 = q2 + selectedRoasterID;
  318. q2 = q2 + ", :duration, :approval, NULL, NULL, NULL, NULL, '{";
  319. q2 = q2 + fileno;
  320. q2 = q2 + "}')";
  321. query2 = new QSqlQuery();
  322. query2.prepare(q2);
  323. query2.bind(":time", timefield.text);
  324. query2.bind(":annotation", notes.plainText);
  325. query2.bind(":duration", duration.text);
  326. query2.bind(":approval", approval.checked);
  327. query2.exec();
  328. query2 = query2.invalidate();
  329. if(target.checked) {
  330. var q3 = "INSERT INTO item_files VALUES(:time, :item, '{";
  331. q3 = q3 + fileno;
  332. q3 = q3 + "}')";
  333. query.prepare(q3);
  334. query.bind(":time", timefield.text);
  335. query.bind(":item", roasted.currentData());
  336. query.exec();
  337. }
  338. query = query.invalidate();
  339. batch.windowModified = false;
  340. batch.close();
  341. });
  342. ]]>
  343. </program>
  344. </window>