Typica is a free program for professional coffee roasters. https://typica.us
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

newsamplebatch.xml 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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 = "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 = "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. roastButton.clicked.connect(function() {
  183. var lc = 1;
  184. currentBatchInfo = batch;
  185. query = new QSqlQuery();
  186. 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)";
  187. query.prepare(q);
  188. query.bind(":name", profileName.currentText);
  189. query.bind(":again", profileName.currentText);
  190. query.exec();
  191. if(query.next())
  192. {
  193. var file = query.value(0);
  194. query.prepare("SELECT file FROM files WHERE id = :id");
  195. query.bind(":id", file);
  196. query.exec();
  197. if(query.next())
  198. {
  199. var targetseries = -1;
  200. var buffer = new QBuffer(query.value(0));
  201. var input = new XMLInput(buffer, 1);
  202. var graph = findChildObject(navigationwindow.loggingWindow, 'graph');
  203. var log = findChildObject(navigationwindow.loggingWindow, 'log');
  204. log.clear();
  205. graph.clear();
  206. input.newTemperatureColumn.connect(function(col, text) {
  207. log.setHeaderData(col, text);
  208. if(text == navigationwindow.loggingWindow.targetcolumnname)
  209. {
  210. targetseries = col;
  211. }
  212. });
  213. input.newAnnotationColumn.connect(log.setHeaderData);
  214. input.measure.connect(graph.newMeasurement);
  215. input.measure.connect(log.newMeasurement);
  216. input.measure.connect(function(data, series) {
  217. if(series == targetseries)
  218. {
  219. targetDetector.newMeasurement(data);
  220. }
  221. });
  222. input.annotation.connect(log.newAnnotation);
  223. input.lastColumn.connect(function(c) {
  224. lc = c;
  225. QSettings.setValue("liveColumn", c+1);
  226. navigationwindow.loggingWindow.postLoadColumnSetup(c);
  227. });
  228. navigationwindow.loggingWindow.raise();
  229. navigationwindow.loggingWindow.activateWindow();
  230. input.input();
  231. log.newAnnotation("End", 1, lc);
  232. }
  233. }
  234. query = query.invalidate();
  235. var t = "Typica - Sample Roasting: [*]" + name.text;
  236. if(profileName.currentText != '')
  237. {
  238. t = t + ", " + profileName.currentText;
  239. }
  240. navigationwindow.loggingWindow.windowTitle = t;
  241. });
  242. var notes = findChildObject(this, 'annotation');
  243. var machine = findChildObject(this, 'machine');
  244. var duration = findChildObject(this, 'duration');
  245. var arrival = findChildObject(this, 'date');
  246. var vendor = findChildObject(this, 'vendor');
  247. var attributes = findChildObject(this, 'attributes');
  248. var target = findChildObject(this, 'target');
  249. submit.clicked.connect(function() {
  250. query = new QSqlQuery();
  251. query.prepare("INSERT INTO files VALUES(DEFAULT, :name, 'profile', NULL, :data) RETURNING id");
  252. query.bind(":name", timefield.text + " " + name.text + " " + profileName.currentText);
  253. query.bindFileData(":data", batch.tempData);
  254. query.exec();
  255. query.next();
  256. var fileno = query.value(0);
  257. var file = new QFile(batch.tempData);
  258. file.remove();
  259. if(target.checked)
  260. {
  261. query.prepare("INSERT INTO sample_roast_profiles VALUES(:time, :name, :file)");
  262. query.bind(":time", timefield.text);
  263. query.bind(":name", profileName.currentText);
  264. query.bind(":file", Number(fileno));
  265. query.exec();
  266. }
  267. var attnames = sqlToArray(attributes.columnArray(0, 0));
  268. for(var i = 0; i < attnames.length; i++)
  269. {
  270. var attname = attnames[i];
  271. if(attname[0] == '{') {
  272. attname = attname.substr(1);
  273. }
  274. if(attname[0] == ' ') {
  275. attname = attname.substr(1);
  276. }
  277. if(attname[attname.length -1] == '}') {
  278. attname = attname.substr(0, attname.length - 2);
  279. }
  280. if(attname.length == 0) {
  281. break;
  282. }
  283. query.prepare("SELECT id FROM item_attributes WHERE name = :name");
  284. query.bind(":name", attnames[i]);
  285. query.exec();
  286. if(query.next())
  287. {
  288. attributes.setData(i, 0, query.value(0), 32);
  289. }
  290. else
  291. {
  292. query.prepare("INSERT INTO item_attributes VALUES(DEFAULT, :name) RETURNING id");
  293. query.bind(":name", attnames[i]);
  294. query.exec();
  295. query.next();
  296. attributes.setData(i, 0, query.value(0), 32);
  297. }
  298. }
  299. query.prepare("INSERT INTO coffee_sample_items VALUES(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Green Sample', :arrival, :vendor, :attrids, :attrvals, NULL) RETURNING id");
  300. query.bind(":name", name.text);
  301. query.bind(":arrival", arrival.date);
  302. query.bind(":vendor", vendor.text);
  303. query.bind(":attrids", attributes.bindableColumnArray(0, 32));
  304. query.bind(":attrvals", attributes.bindableQuotedColumnArray(1, 0));
  305. query.exec();
  306. query.next();
  307. var greenId = query.value(0);
  308. query.prepare("INSERT INTO items VALUES(DEFAULT, :name, NULL, 'lb', 0, 'Coffee: Roasted Sample') RETURNING id");
  309. query.bind(":name", name.text + " " + profileName.currentText);
  310. query.exec();
  311. query.next();
  312. var roastedId = query.value(0);
  313. query.prepare("INSERT INTO roasting_log VALUES(:time, :unroastedids, NULL, :green, :roastedid, :roasted, 'SAMPLEROAST', :note, :machine, :duration, TRUE, NULL, NULL, NULL, NULL, :files)");
  314. query.bind(":time", timefield.text);
  315. query.bind(":unroastedids", "{" + greenId + "}");
  316. query.bind(":green", convertToPounds(parseFloat(green.text), GunitBox.currentText));
  317. query.bind(":roastedid", Number(roastedId));
  318. query.bind(":roasted", convertToPounds(parseFloat(roasted.text), GunitBox.currentText));
  319. query.bind(":note", notes.plainText);
  320. query.bind(":machine", Number(selectedRoastedID));
  321. query.bind(":duration", duration.text);
  322. query.bind(":files", "{" + fileno + "}");
  323. query.exec();
  324. query = query.invalidate();
  325. batch.close();
  326. });
  327. ]]>
  328. </program>
  329. </window>