Typica is a free program for professional coffee roasters. https://typica.us
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

newsamplebatch.xml 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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. </layout>
  64. <label>Notes:</label>
  65. <textarea id="annotation" />
  66. <layout type="horizontal">
  67. <button name="Roast" type="push" id="load" />
  68. <button name="Submit" type="push" id="submit" />
  69. </layout>
  70. <button name="Save log as target profile" type="check" id="target" />
  71. <stretch />
  72. </layout>
  73. <layout type="vertical">
  74. <label>Connected Scales</label>
  75. <layout type="vertical" id="scales" />
  76. <stretch />
  77. </layout>
  78. </layout>
  79. <program>
  80. <![CDATA[
  81. var machine = findChildObject(this, 'machine');
  82. machine.setText(selectedRoasterName + " (" + selectedRoasterID + ")");
  83. var GunitBox = findChildObject(this, 'Gunits');
  84. GunitBox.addItem("g");
  85. GunitBox.addItem("Kg");
  86. GunitBox.addItem("oz");
  87. GunitBox.addItem("lb");
  88. var scalesLayout = findChildObject(this, 'scales');
  89. scalesLayout.spacing = 10;
  90. if(navigationwindow.loggingWindow.scales.length > 0)
  91. {
  92. for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++)
  93. {
  94. var scale = navigationwindow.loggingWindow.scales[i];
  95. var label = new DragLabel();
  96. var weighButton = new QPushButton();
  97. weighButton.text = "Weigh";
  98. weighButton.clicked.connect(scale.weigh);
  99. label.updateMeasurement = function(m, u) {
  100. switch(GunitBox.currentIndex) {
  101. case 0:
  102. this.text = Units.convertWeight(m, u, Units.Gram).toFixed(1);
  103. break;
  104. case 1:
  105. this.text = Units.convertWeight(m, u, Units.Kilogram).toFixed(4);
  106. break;
  107. case 2:
  108. this.text = Units.convertWeight(m, u, Units.Ounce).toFixed(3);
  109. break;
  110. case 3:
  111. this.text = Units.convertWeight(m, u, Units.Pound).toFixed(4);
  112. break;
  113. }
  114. };
  115. scalesLayout.addWidget(label);
  116. scalesLayout.addWidget(weighButton);
  117. scale.newMeasurement.connect(function(m, u) {
  118. label.updateMeasurement(m, u);
  119. });
  120. scale.weigh();
  121. GunitBox['currentIndexChanged(int)'].connect(scale.weigh);
  122. }
  123. }
  124. var submit = findChildObject(this, 'submit');
  125. submit.setEnabled(false);
  126. this.windowTitle = "Typica - New Sample Roasting Batch";
  127. var newMenu = findChildObject(this, 'new');
  128. newMenu.triggered.connect(function() {
  129. createWindow("sampleRoastingBatch");
  130. });
  131. var batch = this;
  132. batch.submitButton = submit;
  133. var name = findChildObject(this, 'name');
  134. var green = findChildObject(this, 'greenWt');
  135. var roasted = findChildObject(this, 'roastedWt');
  136. var loss = findChildObject(this, 'loss');
  137. var timefield = findChildObject(this, 'time');
  138. var convertToPounds = function(w, u) {
  139. switch(u)
  140. {
  141. case "g":
  142. return w * 0.0022;
  143. case "oz":
  144. return w * 0.0625;
  145. case "Kg":
  146. return w * 2.2;
  147. }
  148. return w;
  149. };
  150. var updateWeightLoss = function() {
  151. var cgreen = parseFloat(green.text);
  152. var croast = parseFloat(roasted.text);
  153. if(cgreen > 0)
  154. {
  155. if(croast > 0)
  156. {
  157. loss.text = (((cgreen - croast) / cgreen) * 100).toFixed(2) + "%";
  158. }
  159. else
  160. {
  161. loss.text = "100%";
  162. }
  163. }
  164. };
  165. green.textChanged.connect(function() {
  166. updateWeightLoss();
  167. });
  168. roasted.textChanged.connect(function() {
  169. updateWeightLoss();
  170. });
  171. var roastButton = findChildObject(this, 'load');
  172. var profileName = findChildObject(this, 'profile');
  173. var greenName = findChildObject(this, 'name');
  174. roastButton.clicked.connect(function() {
  175. var stop = findChildObject(navigationwindow.loggingWindow, 'stopbutton');
  176. stop.clicked.connect(function() {
  177. submit.setEnabled(true);
  178. });
  179. var lc = 1;
  180. currentBatchInfo = batch;
  181. query = new QSqlQuery();
  182. 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)";
  183. query.prepare(q);
  184. query.bind(":name", profileName.currentText);
  185. query.bind(":again", profileName.currentText);
  186. query.exec();
  187. if(query.next())
  188. {
  189. var file = query.value(0);
  190. query.prepare("SELECT file FROM files WHERE id = :id");
  191. query.bind(":id", file);
  192. query.exec();
  193. if(query.next())
  194. {
  195. var targetseries = -1;
  196. var buffer = new QBuffer(query.value(0));
  197. var input = new XMLInput(buffer, 1);
  198. var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
  199. var log = findChildObject(navigationwindow.loggingWindow, 'log');
  200. log.clear();
  201. graph.clear();
  202. input.newTemperatureColumn.connect(function(col, text) {
  203. log.setHeaderData(col, text);
  204. if(text == navigationwindow.loggingWindow.targetcolumnname)
  205. {
  206. targetseries = col;
  207. }
  208. });
  209. input.newAnnotationColumn.connect(log.setHeaderData);
  210. input.measure.connect(graph.newMeasurement);
  211. input.measure.connect(log.newMeasurement);
  212. input.measure.connect(function(data, series) {
  213. if(series == targetseries)
  214. {
  215. targetDetector.newMeasurement(data);
  216. }
  217. });
  218. input.annotation.connect(log.newAnnotation);
  219. input.lastColumn.connect(function(c) {
  220. lc = c;
  221. QSettings.setValue("liveColumn", c+1);
  222. navigationwindow.loggingWindow.postLoadColumnSetup(c);
  223. });
  224. navigationwindow.loggingWindow.raise();
  225. navigationwindow.loggingWindow.activateWindow();
  226. input.input();
  227. log.newAnnotation("End", 1, lc);
  228. }
  229. }
  230. query = query.invalidate();
  231. var t = "Typica - Sample Roasting: [*]" + name.text;
  232. if(profileName.currentText != '')
  233. {
  234. t = t + ", " + profileName.currentText;
  235. }
  236. navigationwindow.loggingWindow.windowTitle = t;
  237. });
  238. var notes = findChildObject(this, 'annotation');
  239. var machine = findChildObject(this, 'machine');
  240. var duration = findChildObject(this, 'duration');
  241. var arrival = findChildObject(this, 'date');
  242. var vendor = findChildObject(this, 'vendor');
  243. var attributes = findChildObject(this, 'attributes');
  244. var target = findChildObject(this, 'target');
  245. submit.clicked.connect(function() {
  246. query = new QSqlQuery();
  247. query.prepare("INSERT INTO files VALUES(default, :name, 'profile', NULL, :data) RETURNING id");
  248. query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);
  249. query.bindFileData(":data", batch.tempData);
  250. query.exec();
  251. query.next();
  252. var fileno = query.value(0);
  253. var file = new QFile(batch.tempData);
  254. file.remove();
  255. if(target.checked)
  256. {
  257. query.prepare("INSERT INTO sample_roast_profiles VALUES(:time, :name, :file)");
  258. query.bind(":time", timefield.text);
  259. query.bind(":name", profileName.currentText);
  260. query.bind(":file", Number(fileno));
  261. query.exec();
  262. }
  263. var attnames = sqlToArray(attributes.columnArray(0, 0));
  264. for(var i = 0; i < attnames.length; i++)
  265. {
  266. query.prepare("SELECT id FROM item_attributes WHERE name = :name");
  267. query.bind(":name", attnames[i]);
  268. query.exec();
  269. if(query.next())
  270. {
  271. attributes.setData(i, 0, query.value(0), 32);
  272. }
  273. else
  274. {
  275. query.prepare("INSERT INTO item_attributes VALUES(DEFAULT, :name) RETURNING id");
  276. query.bind(":name", attnames[i]);
  277. query.exec();
  278. query.next();
  279. attributes.setData(i, 0, query.value(0), 32);
  280. }
  281. }
  282. query.prepare("INSERT INTO coffee_sample_items(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Green Sample', :arrival, :vendor, :attrids, :attrvals, NULL) RETURNING id");
  283. query.bind(":name", name.text);
  284. query.bind(":arrival", arrival.date);
  285. query.bind(":vendor", vendor.text);
  286. query.bind(":attrids", attributes.bindableColumnArray(0, 32));
  287. query.bind(":attrvals", attributes.bindableQuotedColumnArray(1, 0));
  288. query.exec();
  289. query.next();
  290. var greenId = query.value(0);
  291. query.prepare("INSERT INTO items(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Roasted Sample') RETURNING id");
  292. query.bind(":name", name.text + " " + profileName.currentText);
  293. query.exec();
  294. query.next();
  295. var roastedId = query.value(0);
  296. query.prepare("INSERT INTO roasting_log VALUES(:time, :unroastedids, NULL, :green, :roastedid, :roasted, 'SAMPLEROAST', :note, :machine, :duration, TRUE, NULL, NULL, NULL, NULL, :files)");
  297. query.bind(":time", timefield.text);
  298. query.bind(":unroastedids", "{" + greenId + "}");
  299. query.bind(":green", convertToPounds(parseFloat(green.text), GunitBox.currentText));
  300. query.bind(":roastedid", Number(roastedId));
  301. query.bind(":roasted", convertToPounds(parseFloat(roasted.text), GunitBox.currentText));
  302. query.bind(":note", notes.plainText);
  303. query.bind(":machine", Number(selectedRoastedID));
  304. query.bind(":duration", duration.text);
  305. query.bind(":files", "{" + fileno + "}");
  306. query.exec();
  307. query = query.invalidate();
  308. batch.close();
  309. });
  310. ]]>
  311. </program>
  312. </window>