Lemonade Laboratory

Measure the concentration of lemonade in a sample.

by Wouter Vos

HTML

<!--script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"-->
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>

<body class="container">
<div>
	<h1>Calibration Line - iVention Development Case</h1>
	<p>Calibration Formula: y = ax + b</p>
	<p>a = unknown</p>
	<p>b = unknown</p>
	<p>Results lemonade testing</p>
	<table>
		<thead>
			<tr class="tableHeader">
			  <td class="col-sm-3">X Concentration</td>
			  <td class="col-sm-3">Y (theoretical)</td>
			  <td class="col-sm-3">Y (measured)</td>
			  <td>Include</td>
			</tr>
		</thead>
		<tbody data-bind="foreach: calibrations">
			<tr>
			  <td><input type="number" step="any" class="col-sm-3" data-bind="numeric, value: x" /></td>
			  <td><input type="number" step="any" class="col-sm-3" data-bind="numeric, value: yT" /></td>
			  <td><input type="number" step="any" class="col-sm-3" data-bind="numeric, value: yM" /></td>
			  <td><input type="checkbox" data-bind="checked: included" /></td>
			  <td><button class="col-sm-2" data-bind="click: $parent.remove">Remove</button></td>
			</tr>
		</tbody>
	</table>
	<p><button data-bind="click: add">Add Calibration</button></p>
	<p>
	  <label for="sample">Measured value in sample:</label>
	  <input id="sample" type="number" step="any" class="col-sm-4" data-bind='value: sampleValue' />
	</p>
	<p>
		<button click="calculate">Calculate values</button><br />
		<label for="addition" class="col-sm-4">Unknown a (Addition):</label>
		<input readonly id="addition" type="number" step="any" class="col-sm-4" data-bind='value: addition' />
		<label for="factor" class="col-sm-4">Unknown b (Factor):</label>
		<input readonly id="factor" type="number" step="any" class="col-sm-4" data-bind='value: factor' />
		<label for="concentration" class="col-sm-4">Concentration:</label>
		<input readonly id="concentration" type="number" step="any" class="col-sm-4"...

CSS

@import url('https://cdnjs.cloudflare.com/ajax/libs/jquery-ui-bootstrap/0.5pre/assets/css/bootstrap.min.css');
@import url('https://cdnjs.cloudflare.com/ajax/libs/jquery-ui-bootstrap/0.5pre/assets/css/font-awesome.min.css');

body { font-family: Verdana; font-size: 14px; }

.container {
    margin-top: 10px;
    margin-left: 10px;
}

.tableHeader {
  font-weight: bold;
}

JavaScript

debugger;// Here's my data model
var ViewModel = function() {
	var self = this;
	self.concentration = ko.observable();
	self.calibrations = ko.observableArray();
	self.inclusions = ko.observableArray();
	self.sampleValue = ko.observable();
	self.addition = ko.observable();
	self.factor = ko.observable();
	
	self.sampleValue.subscribe(function(newValue) {
		self.calculate();
	});
    
    self.add = function() {
        self.calibrations.push({
			// Data-binding expects the object to have these properties.
            x: undefined,
            yT: undefined,
            yM: undefined,
			included: true
        });
    };
    
    self.remove = function(item) {
        self.calibrations.remove(item);
    };
 
 	// Find the concentration of the measured sample.
	self.calculate = function() {
		self.calibrations().forEach(function(cal) {
			// User input is in string type, convert to number.
			cal.x = Number(cal.x);
			cal.yT = Number(cal.yT);
			cal.yM = Number(cal.yM);
			
			if(cal.included) {
				self.inclusions.push(cal);		
		})
	
		var xsum = 0, x2sum = 0, ysum = 0, xysum = 0;
		self.inclusions().forEach(function(cal) {
				xsum += cal.x;
				ysum += cal.yM;
				x2sum += cal.x * cal.x;
				xysum += cal.x * cal.yM;
			}
		});

		var n = self.inclusions().length; // number of measurements
		self.factor((n * xysum - xsum * ysum) / (n * x2sum - xsum * xsum));
		self.addition((x2sum * ysum - xsum * xysum) / (x2sum * n - xsum * xsum));

		self.concentration((self.sampleValue() - self.addition()) / self.factor());
		self.drawChart();
	};
 
	self.drawChart = function() {
		var lastx = 0;
		var data = {
			theoretical: [],
			measured: []
		};
  
		self.inclusions().forEach(function(cal) {
			data.theoretical.push({ x: cal.x, y: cal.yT });
			data.measured.push({ x: cal.x, y: cal.yM });
			lastx = cal.x;
		});
    
		// Line that on average is closest to all measured points
		var calibrationLine = [
			[0, self.addition()],
			[lastx, self.addition() + self.factor() *...