Typica is a free program for professional coffee roasters. https://typica.us
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

newsamplebatch.xml 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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. this.endBatch = function() {};
  136. var batch = this;
  137. batch.submitButton = submit;
  138. var name = findChildObject(this, 'name');
  139. var green = findChildObject(this, 'greenWt');
  140. var roasted = findChildObject(this, 'roastedWt');
  141. var loss = findChildObject(this, 'loss');
  142. var timefield = findChildObject(this, 'time');
  143. var convertToPounds = function(w, u) {
  144. switch(u)
  145. {
  146. case "g":
  147. return w * 0.0022;
  148. case "oz":
  149. return w * 0.0625;
  150. case "Kg":
  151. return w * 2.2;
  152. }
  153. return w;
  154. };
  155. var updateWeightLoss = function() {
  156. var cgreen = parseFloat(green.text);
  157. var croast = parseFloat(roasted.text);
  158. if(cgreen > 0)
  159. {
  160. if(croast > 0)
  161. {
  162. loss.text = (((cgreen - croast) / cgreen) * 100).toFixed(2) + "%";
  163. }
  164. else
  165. {
  166. loss.text = "100%";
  167. }
  168. }
  169. };
  170. green.textChanged.connect(function() {
  171. updateWeightLoss();
  172. });
  173. roasted.textChanged.connect(function() {
  174. updateWeightLoss();
  175. });
  176. var roastButton = findChildObject(this, 'load');
  177. var profileName = findChildObject(this, 'profile');
  178. var greenName = findChildObject(this, 'name');
  179. var stop = findChildObject(navigationwindow.loggingWindow, 'stopbutton');
  180. stop.clicked.connect(function() {
  181. submit.setEnabled(true);
  182. });
  183. var validateCapacity = function() {
  184. if(checkCapacity == "true") {
  185. if(convertToPounds(parseFloat(green.text), GunitBox.currentText) > poundsCapacity) {
  186. return false;
  187. }
  188. }
  189. return true;
  190. }
  191. roastButton.clicked.connect(function() {
  192. var proceed = false;
  193. if(validateCapacity()) {
  194. proceed = true;
  195. } else {
  196. proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
  197. TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
  198. }
  199. if(proceed) {
  200. doRoast();
  201. }
  202. });
  203. var doRoast = function() {
  204. var lc = 1;
  205. currentBatchInfo = batch;
  206. query = new QSqlQuery();
  207. 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)";
  208. query.prepare(q);
  209. query.bind(":name", profileName.currentText);
  210. query.bind(":again", profileName.currentText);
  211. query.exec();
  212. if(query.next())
  213. {
  214. var file = query.value(0);
  215. query.prepare("SELECT file FROM files WHERE id = :id");
  216. query.bind(":id", file);
  217. query.exec();
  218. if(query.next())
  219. {
  220. var targetseries = -1;
  221. var buffer = new QBuffer(query.value(0));
  222. var input = new XMLInput(buffer, 1);
  223. var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
  224. var log = findChildObject(navigationwindow.loggingWindow, 'log');
  225. log.clear();
  226. graph.clear();
  227. input.newTemperatureColumn.connect(function(col, text) {
  228. log.setHeaderData(col, text);
  229. if(text == navigationwindow.loggingWindow.targetcolumnname)
  230. {
  231. targetseries = col;
  232. }
  233. });
  234. input.newAnnotationColumn.connect(log.setHeaderData);
  235. input.measure.connect(graph.newMeasurement);
  236. input.measure.connect(log.newMeasurement);
  237. input.measure.connect(function(data, series) {
  238. if(series == targetseries)
  239. {
  240. targetDetector.newMeasurement(data);
  241. }
  242. });
  243. input.annotation.connect(log.newAnnotation);
  244. input.lastColumn.connect(function(c) {
  245. lc = c;
  246. QSettings.setValue("liveColumn", c+1);
  247. navigationwindow.loggingWindow.postLoadColumnSetup(c);
  248. });
  249. navigationwindow.loggingWindow.raise();
  250. navigationwindow.loggingWindow.activateWindow();
  251. log.updatesEnabled = false;
  252. graph.updatesEnabled = false;
  253. input.input();
  254. graph.updatesEnabled = true;
  255. log.updatesEnabled = true;
  256. log.newAnnotation(TTR("sampleRoastingBatch", "End"), 1, lc);
  257. }
  258. }
  259. query = query.invalidate();
  260. var t = TTR("sampleRoastingBatch", "Typica - Sample Roasting: [*]") + name.text;
  261. if(profileName.currentText != '')
  262. {
  263. t = t + ", " + profileName.currentText;
  264. }
  265. navigationwindow.loggingWindow.windowTitle = t;
  266. };
  267. var notes = findChildObject(this, 'annotation');
  268. var machine = findChildObject(this, 'machine');
  269. var duration = findChildObject(this, 'duration');
  270. var arrival = findChildObject(this, 'date');
  271. var vendor = findChildObject(this, 'vendor');
  272. var attributes = findChildObject(this, 'attributes');
  273. var target = findChildObject(this, 'target');
  274. submit.clicked.connect(function() {
  275. var proceed = false;
  276. if(validateCapacity()) {
  277. proceed = true;
  278. } else {
  279. proceed = displayWarning(TTR("sampleRoastingBatch", "Suspicious Input"),
  280. TTR("sampleRoastingBatch", "Entered green coffee weight exceeds maximum batch size. Continue?"));
  281. }
  282. if(proceed) {
  283. doSubmit();
  284. }
  285. });
  286. var doSubmit = function() {
  287. query = new QSqlQuery();
  288. query.prepare("INSERT INTO files (id, name, type, note, file) VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
  289. query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);
  290. query.bindFileData(":data", batch.tempData);
  291. query.exec();
  292. query.next();
  293. var fileno = query.value(0);
  294. var file = new QFile(batch.tempData);
  295. file.remove();
  296. if(target.checked)
  297. {
  298. query.prepare("INSERT INTO sample_roast_profiles (time, profile_name, file) VALUES(:time, :name, :file)");
  299. query.bind(":time", timefield.text);
  300. query.bind(":name", profileName.currentText);
  301. query.bind(":file", Number(fileno));
  302. query.exec();
  303. }
  304. var attnames = sqlToArray(attributes.columnArray(0, 0));
  305. for(var i = 0; i < attnames.length; i++)
  306. {
  307. var attname = attnames[i];
  308. if(attname[0] == '{') {
  309. attname = attname.substr(1);
  310. }
  311. if(attname[0] == ' ') {
  312. attname = attname.substr(1);
  313. }
  314. if(attname[attname.length -1] == '}') {
  315. attname = attname.substr(0, attname.length - 1);
  316. }
  317. if(attname.length == 0) {
  318. break;
  319. }
  320. query.prepare("SELECT id FROM item_attributes WHERE name = :name");
  321. query.bind(":name", attname);
  322. query.exec();
  323. if(query.next())
  324. {
  325. attributes.setData(i, 0, query.value(0), 32);
  326. }
  327. else
  328. {
  329. query.prepare("INSERT INTO item_attributes (id, name) VALUES(DEFAULT, :name) RETURNING id");
  330. query.bind(":name", attname);
  331. query.exec();
  332. query.next();
  333. attributes.setData(i, 0, query.value(0), 32);
  334. }
  335. }
  336. 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");
  337. query.bind(":name", name.text);
  338. query.bind(":arrival", arrival.date);
  339. query.bind(":vendor", vendor.text);
  340. query.bind(":attrids", attributes.bindableColumnArray(0, 32));
  341. query.bind(":attrvals", attributes.bindableQuotedColumnArray(1, 0));
  342. query.exec();
  343. query.next();
  344. var greenId = query.value(0);
  345. query.prepare("INSERT INTO items (id, name, reference, unit, quantity, category) VALUES(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Roasted Sample') RETURNING id");
  346. query.bind(":name", name.text + " " + profileName.currentText);
  347. query.exec();
  348. query.next();
  349. var roastedId = query.value(0);
  350. 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)");
  351. query.bind(":time", timefield.text);
  352. query.bind(":unroastedids", "{" + greenId + "}");
  353. query.bind(":greens", "{" + convertToPounds(parseFloat(green.text), GunitBox.currentText) + "}");
  354. query.bind(":green", convertToPounds(parseFloat(green.text), GunitBox.currentText));
  355. query.bind(":roastedid", Number(roastedId));
  356. query.bind(":roasted", convertToPounds(parseFloat(roasted.text), GunitBox.currentText));
  357. query.bind(":note", notes.plainText);
  358. query.bind(":machine", Number(selectedRoasterID));
  359. query.bind(":duration", duration.text);
  360. query.bind(":files", "{" + fileno + "}");
  361. query.exec();
  362. query = query.invalidate();
  363. batch.close();
  364. }
  365. ]]>
  366. </program>
  367. </window>