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.

newsamplebatch.xml 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. <window id="sampleRoastingBatch">
  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. <label>Sample Details:</label>
  8. <layout type="grid">
  9. <row>
  10. <column><label>Name:</label></column>
  11. <column><line id="name" /></column>
  12. </row>
  13. <row>
  14. <column><label>Vendor:</label></column>
  15. <column><line id="vendor" /></column>
  16. </row>
  17. <row>
  18. <column><label>Date:</label></column>
  19. <column><calendar id="date" /></column>
  20. </row>
  21. </layout>
  22. <label>Optional Details:</label>
  23. <sqltablearray columns="2" id="attributes">
  24. <column name="Attribute" />
  25. <column name="Value" />
  26. </sqltablearray>
  27. <stretch />
  28. </layout>
  29. <layout type="vertical">
  30. <label>Roasting Details:</label>
  31. <layout type="grid">
  32. <row>
  33. <column><label>Machine:</label></column>
  34. <column><line id="machine" writable="false" /></column>
  35. </row>
  36. <row>
  37. <column><label>Target Profile:</label></column>
  38. <column>
  39. <sqldrop data="0" display="0" showdata="false" editable="true" id="profile">
  40. <query>SELECT DISTINCT profile_name FROM sample_roast_profiles UNION SELECT '' ORDER BY profile_name ASC</query>
  41. </sqldrop>
  42. </column>
  43. </row>
  44. <row>
  45. <column><label>Green Weight:</label></column>
  46. <column><line id="greenWt" /></column>
  47. <column>
  48. <sqldrop data="0" display="0" showdata="false" editable="false" id="Gunits" />
  49. </column>
  50. </row>
  51. <row>
  52. <column><label>Roasted Weight:</label></column>
  53. <column><line id="roastedWt" /></column>
  54. </row>
  55. <row>
  56. <column><label>Weight Loss:</label></column>
  57. <column><line writable="false" id="loss" /></column>
  58. </row>
  59. <row>
  60. <column><label>Time:</label></column>
  61. <column><line id="time" writable="false" /></column>
  62. </row>
  63. <row>
  64. <column><label>Duration:</label></column>
  65. <column><line id="duration" writable="false" /></column>
  66. </row>
  67. </layout>
  68. <label>Notes:</label>
  69. <textarea id="annotation" />
  70. <layout type="horizontal">
  71. <button name="Roast" type="push" id="load" />
  72. <button name="Submit" type="push" id="submit" />
  73. </layout>
  74. <button name="Save log as target profile" type="check" id="target" />
  75. <stretch />
  76. </layout>
  77. <layout type="vertical">
  78. <label>Connected Scales</label>
  79. <layout type="vertical" id="scales" />
  80. <stretch />
  81. </layout>
  82. </layout>
  83. <program>
  84. <![CDATA[
  85. var machine = findChildObject(this, 'machine');
  86. machine.setText(selectedRoasterName + " (" + selectedRoasterID + ")");
  87. var GunitBox = findChildObject(this, 'Gunits');
  88. GunitBox.addItem("g");
  89. GunitBox.addItem("Kg");
  90. GunitBox.addItem("oz");
  91. GunitBox.addItem("lb");
  92. var scalesLayout = findChildObject(this, 'scales');
  93. scalesLayout.spacing = 10;
  94. if(navigationwindow.loggingWindow.scales.length > 0)
  95. {
  96. for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++)
  97. {
  98. var scale = navigationwindow.loggingWindow.scales[i];
  99. var label = new DragLabel();
  100. var weighButton = new QPushButton();
  101. weighButton.text = TTR("sampleRoastingBatch", "Weigh");
  102. weighButton.clicked.connect(scale.weigh);
  103. label.updateMeasurement = function(m, u) {
  104. switch(GunitBox.currentIndex) {
  105. case 0:
  106. this.text = Units.convertWeight(m, u, Units.Gram).toFixed(1);
  107. break;
  108. case 1:
  109. this.text = Units.convertWeight(m, u, Units.Kilogram).toFixed(4);
  110. break;
  111. case 2:
  112. this.text = Units.convertWeight(m, u, Units.Ounce).toFixed(3);
  113. break;
  114. case 3:
  115. this.text = Units.convertWeight(m, u, Units.Pound).toFixed(4);
  116. break;
  117. }
  118. };
  119. scalesLayout.addWidget(label);
  120. scalesLayout.addWidget(weighButton);
  121. scale.newMeasurement.connect(function(m, u) {
  122. label.updateMeasurement(m, u);
  123. });
  124. scale.weigh();
  125. GunitBox['currentIndexChanged(int)'].connect(scale.weigh);
  126. }
  127. }
  128. var submit = findChildObject(this, 'submit');
  129. submit.setEnabled(false);
  130. this.windowTitle = TTR("sampleRoastingBatch", "Typica - New Sample Roasting Batch");
  131. var newMenu = findChildObject(this, 'new');
  132. newMenu.triggered.connect(function() {
  133. createWindow("sampleRoastingBatch");
  134. });
  135. var batch = this;
  136. batch.submitButton = submit;
  137. var name = findChildObject(this, 'name');
  138. var green = findChildObject(this, 'greenWt');
  139. var roasted = findChildObject(this, 'roastedWt');
  140. var loss = findChildObject(this, 'loss');
  141. var timefield = findChildObject(this, 'time');
  142. var convertToPounds = function(w, u) {
  143. switch(u)
  144. {
  145. case "g":
  146. return w * 0.0022;
  147. case "oz":
  148. return w * 0.0625;
  149. case "Kg":
  150. return w * 2.2;
  151. }
  152. return w;
  153. };
  154. var updateWeightLoss = function() {
  155. var cgreen = parseFloat(green.text);
  156. var croast = parseFloat(roasted.text);
  157. if(cgreen > 0)
  158. {
  159. if(croast > 0)
  160. {
  161. loss.text = (((cgreen - croast) / cgreen) * 100).toFixed(2) + "%";
  162. }
  163. else
  164. {
  165. loss.text = "100%";
  166. }
  167. }
  168. };
  169. green.textChanged.connect(function() {
  170. updateWeightLoss();
  171. });
  172. roasted.textChanged.connect(function() {
  173. updateWeightLoss();
  174. });
  175. var roastButton = findChildObject(this, 'load');
  176. var profileName = findChildObject(this, 'profile');
  177. var greenName = findChildObject(this, 'name');
  178. var stop = findChildObject(navigationwindow.loggingWindow, 'stopbutton');
  179. stop.clicked.connect(function() {
  180. submit.setEnabled(true);
  181. });
  182. var validateCapacity = function() {
  183. if(checkCapacity == "true") {
  184. if(convertToPounds(parseFloat(green.text), GunitBox.currentText) > poundsCapacity) {
  185. return false;
  186. }
  187. }
  188. return true;
  189. }
  190. roastButton.clicked.connect(function() {
  191. var proceed = false;
  192. if(validateCapacity()) {
  193. proceed = true;
  194. } else {
  195. proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
  196. TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
  197. }
  198. if(proceed) {
  199. doRoast();
  200. }
  201. });
  202. var doRoast = function() {
  203. var lc = 1;
  204. currentBatchInfo = batch;
  205. query = new QSqlQuery();
  206. var q = "SELECT file FROM sample_roast_profiles WHERE profile_name = :name AND time = (SELECT max(time) FROM sample_roast_profiles WHERE profile_name = :again)";
  207. query.prepare(q);
  208. query.bind(":name", profileName.currentText);
  209. query.bind(":again", profileName.currentText);
  210. query.exec();
  211. if(query.next())
  212. {
  213. var file = query.value(0);
  214. query.prepare("SELECT file FROM files WHERE id = :id");
  215. query.bind(":id", file);
  216. query.exec();
  217. if(query.next())
  218. {
  219. var targetseries = -1;
  220. var buffer = new QBuffer(query.value(0));
  221. var input = new XMLInput(buffer, 1);
  222. var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
  223. var log = findChildObject(navigationwindow.loggingWindow, 'log');
  224. log.clear();
  225. graph.clear();
  226. input.newTemperatureColumn.connect(function(col, text) {
  227. log.setHeaderData(col, text);
  228. if(text == navigationwindow.loggingWindow.targetcolumnname)
  229. {
  230. targetseries = col;
  231. }
  232. });
  233. input.newAnnotationColumn.connect(log.setHeaderData);
  234. input.measure.connect(graph.newMeasurement);
  235. input.measure.connect(log.newMeasurement);
  236. input.measure.connect(function(data, series) {
  237. if(series == targetseries)
  238. {
  239. targetDetector.newMeasurement(data);
  240. }
  241. });
  242. input.annotation.connect(log.newAnnotation);
  243. input.lastColumn.connect(function(c) {
  244. lc = c;
  245. QSettings.setValue("liveColumn", c+1);
  246. navigationwindow.loggingWindow.postLoadColumnSetup(c);
  247. });
  248. navigationwindow.loggingWindow.raise();
  249. navigationwindow.loggingWindow.activateWindow();
  250. log.updatesEnabled = false;
  251. graph.updatesEnabled = false;
  252. input.input();
  253. graph.updatesEnabled = true;
  254. log.updatesEnabled = true;
  255. log.newAnnotation(TTR("sampleRoastingBatch", "End"), 1, lc);
  256. }
  257. }
  258. query = query.invalidate();
  259. var t = TTR("sampleRoastingBatch", "Typica - Sample Roasting: [*]") + name.text;
  260. if(profileName.currentText != '')
  261. {
  262. t = t + ", " + profileName.currentText;
  263. }
  264. navigationwindow.loggingWindow.windowTitle = t;
  265. };
  266. var notes = findChildObject(this, 'annotation');
  267. var machine = findChildObject(this, 'machine');
  268. var duration = findChildObject(this, 'duration');
  269. var arrival = findChildObject(this, 'date');
  270. var vendor = findChildObject(this, 'vendor');
  271. var attributes = findChildObject(this, 'attributes');
  272. var target = findChildObject(this, 'target');
  273. submit.clicked.connect(function() {
  274. var proceed = false;
  275. if(validateCapacity()) {
  276. proceed = true;
  277. } else {
  278. proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
  279. TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
  280. }
  281. if(proceed) {
  282. doSubmit();
  283. }
  284. });
  285. var doSubmit = function() {
  286. query = new QSqlQuery();
  287. query.prepare("INSERT INTO files (id, name, type, note, file) VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
  288. query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);
  289. query.bindFileData(":data", batch.tempData);
  290. query.exec();
  291. query.next();
  292. var fileno = query.value(0);
  293. var file = new QFile(batch.tempData);
  294. file.remove();
  295. if(target.checked)
  296. {
  297. query.prepare("INSERT INTO sample_roast_profiles (time, profile_name, file) VALUES(:time, :name, :file)");
  298. query.bind(":time", timefield.text);
  299. query.bind(":name", profileName.currentText);
  300. query.bind(":file", Number(fileno));
  301. query.exec();
  302. }
  303. var attnames = sqlToArray(attributes.columnArray(0, 0));
  304. for(var i = 0; i < attnames.length; i++)
  305. {
  306. var attname = attnames[i];
  307. if(attname[0] == '{') {
  308. attname = attname.substr(1);
  309. }
  310. if(attname[0] == ' ') {
  311. attname = attname.substr(1);
  312. }
  313. if(attname[attname.length -1] == '}') {
  314. attname = attname.substr(0, attname.length - 1);
  315. }
  316. if(attname.length == 0) {
  317. break;
  318. }
  319. query.prepare("SELECT id FROM item_attributes WHERE name = :name");
  320. query.bind(":name", attname);
  321. query.exec();
  322. if(query.next())
  323. {
  324. attributes.setData(i, 0, query.value(0), 32);
  325. }
  326. else
  327. {
  328. query.prepare("INSERT INTO item_attributes (id, name) VALUES(DEFAULT, :name) RETURNING id");
  329. query.bind(":name", attname);
  330. query.exec();
  331. query.next();
  332. attributes.setData(i, 0, query.value(0), 32);
  333. }
  334. }
  335. query.prepare("INSERT INTO coffee_sample_items (id, name, reference, unit, quantity, category, arrival, vendor, attribute_ids, attribute_values) VALUES(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Green Sample', :arrival, :vendor, :attrids, :attrvals) RETURNING id");
  336. query.bind(":name", name.text);
  337. query.bind(":arrival", arrival.date);
  338. query.bind(":vendor", vendor.text);
  339. query.bind(":attrids", attributes.bindableColumnArray(0, 32));
  340. query.bind(":attrvals", attributes.bindableQuotedColumnArray(1, 0));
  341. query.exec();
  342. query.next();
  343. var greenId = query.value(0);
  344. query.prepare("INSERT INTO items (id, name, reference, unit, quantity, category) VALUES(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Roasted Sample') RETURNING id");
  345. query.bind(":name", name.text + " " + profileName.currentText);
  346. query.exec();
  347. query.next();
  348. var roastedId = query.value(0);
  349. query.prepare("INSERT INTO roasting_log (time, unroasted_id, unroasted_quantity, unroasted_total_quantity, roasted_id, roasted_quantity, transaction_type, annotation, machine, duration, approval, humidity, barometric, indoor_air, outdoor_air, files) VALUES(:time, :unroastedids, :greens, :green, :roastedid, :roasted, 'SAMPLEROAST', :note, :machine, :duration, TRUE, NULL, NULL, NULL, NULL, :files)");
  350. query.bind(":time", timefield.text);
  351. query.bind(":unroastedids", "{" + greenId + "}");
  352. query.bind(":greens", "{" + convertToPounds(parseFloat(green.text), GunitBox.currentText) + "}");
  353. query.bind(":green", convertToPounds(parseFloat(green.text), GunitBox.currentText));
  354. query.bind(":roastedid", Number(roastedId));
  355. query.bind(":roasted", convertToPounds(parseFloat(roasted.text), GunitBox.currentText));
  356. query.bind(":note", notes.plainText);
  357. query.bind(":machine", Number(selectedRoasterID));
  358. query.bind(":duration", duration.text);
  359. query.bind(":files", "{" + fileno + "}");
  360. query.exec();
  361. query = query.invalidate();
  362. batch.close();
  363. }
  364. ]]>
  365. </program>
  366. </window>