fannie/item/images/NutriFacts.php
<?php
/*******************************************************************************
Copyright 2013 Whole Foods Community Co-op
This file is part of CORE-POS.
CORE-POS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
CORE-POS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
in the file license.txt along with IT CORE; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*********************************************************************************/
require(dirname(__FILE__) . '/../../config.php');
if (!class_exists('FannieAPI')) {
include(__DIR__ . '/../../classlib2.0/FannieAPI.php');
}
class NutriFacts extends FannieRESTfulPage
{
protected $header = 'NutriFacts Image Builder';
protected $title = 'NutriFacts Image Builder';
public $description = '[NutriFacts Image Builder] generates Nutrition Facts Image';
public $has_unit_tests = true;
private $daily_values = array(
'total_fat' => 65,
'sat_fat' => 20,
'cholest' => 300,
'sodium' => 2400,
'total_carb' => 300,
'fiber' => 25,
'protein' => 50,
);
protected function get_id_handler()
{
$upc = BarcodeLib::padUPC($this->id);
$req = new NutriFactReqItemsModel($this->connection);
$req->upc($upc);
$req->load();
$json = json_decode($req->toJSON(), true);
$json['opts'] = array();
$opt = new NutriFactOptItemsModel($this->connection);
$opt->upc($upc);
foreach ($opt->find('nutriFactOptItemID') as $obj) {
$json['opts'][] = json_decode($obj->toJSON(), true);
}
$prod = new ProductsModel($this->connection);
$prod->upc($upc);
foreach ($prod->find() as $obj) {
$json['description'] = $obj->description();
break;
}
echo json_encode($json);
return false;
}
private function saveForm()
{
$req = new NutriFactReqItemsModel($this->connection);
$req->upc(BarcodeLib::padUPC($this->form->upc));
$req->servingSize($this->form->serving_size);
$req->calories($this->normalizeVal($this->form->calories, ''));
$req->fatCalories($this->normalizeVal($this->form->fat_calories, ''));
$req->totalFat($this->normalizeVal($this->form->total_fat, 'g'));
$req->saturatedFat($this->normalizeVal($this->form->sat_fat, 'g'));
$req->transFat($this->normalizeVal($this->form->trans_fat, 'g'));
$req->cholesterol($this->normalizeVal($this->form->cholest, 'mg'));
$req->sodium($this->normalizeVal($this->form->sodium, 'mg'));
$req->totalCarbs($this->normalizeVal($this->form->total_carb, 'g'));
$req->fiber($this->normalizeVal($this->form->fiber, 'g'));
$req->fiber($this->normalizeVal($this->form->fiber, 'g'));
$req->sugar($this->normalizeVal($this->form->sugar, 'g'));
$req->protein($this->normalizeVal($this->form->protein, 'g'));
$req->save();
$opt = new NutriFactOptItemsModel($this->connection);
$opt->upc($req->upc());
foreach ($opt->find() as $obj) {
$opt->delete();
}
for ($i=0; $i<count($this->form->nutrient); $i++) {
if ($this->form->nutrient[$i] === '') {
continue;
}
$opt->name($this->form->nutrient[$i]);
if ($this->form->percent[$i] === '') {
$opt->percentDV(0);
} else {
$opt->percentDV($this->form->percent[$i]);
}
$opt->save();
}
}
protected function post_handler()
{
$this->saveForm();
$pdf = new FPDF('P', 'in', array(3.5, 1.25));
$pdf->SetMargins(0, 0, 0);
$pdf->SetAutoPageBreak(false);
$pdf->AddPage();
$pdf->SetDrawColor(0, 0, 0);
$edge = 0.02;
$pdf->Rect($edge, $edge, 3.5-(2*$edge), 1.25-(2*$edge));
$space = 0.18;
$offset = 1;
for ($i=0; $i<6; $i++) {
$pdf->Line($offset, $space+($i*$space), 3.5-(3*$edge), $space+($i*$space));
if ($i === 0 || $i === 5) {
$pdf->Line($offset, 0.01+$space+($i*$space), 3.5-(3*$edge), 0.01+$space+($i*$space));
}
}
$pdf->SetFont('Arial', 'B', 15);
$pdf->SetXY(1.5*$edge, 3.5*$edge);
$pdf->MultiCell(1, 0.19, "Nutrition\nFacts:");
$pdf->SetFont('Arial', '', 7);
$pdf->SetXY(1.5*$edge, 24*$edge);
$pdf->Cell(0.5, 0.10, 'Serv. Size');
$pdf->Cell(1, 0.10, $this->form->serving_size);
$pdf->Ln(0.20);
$pdf->SetFont('Arial', 'B', 7);
$pdf->SetX(1.5*$edge);
$pdf->Cell(0.5, 0.10, 'Calories');
$pdf->SetFont('Arial', '', 7);
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->calories, ''));
$pdf->Ln();
$pdf->SetX(3.5*$edge);
$pdf->Cell(0.45, 0.10, 'Fat Cal.');
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->fat_calories, ''));
$pdf->Ln(0.28);
$pdf->SetFont('Arial', '', 4);
$pdf->MultiCell(0.85, 0.08, "*Percent Daily Values (DV) are\nbased on a 2,000 calorie diet");
$columns = array(1.01, 1.9, 2.20, 3);
$pdf->SetFont('Arial', 'B', 6);
$pdf->SetXY($columns[0], 2*$edge);
$pdf->Cell($columns[0], 0.10, 'Amount/serving');
$pdf->SetXY($columns[1], 2*$edge);
$pdf->Cell($columns[1], 0.10, '%DV*');
$pdf->SetXY($columns[2], 2*$edge);
$pdf->Cell($columns[2], 0.10, 'Amount/serving');
$pdf->SetXY($columns[3], 2*$edge);
$pdf->Cell($columns[3], 0.10, '%DV*');
$mid_col = 0.45;
$pdf->SetXY($columns[0], (3*$edge)+($space*1));
$pdf->Cell($columns[0], 0.10, 'Total Fat');
$pdf->SetFont('Arial', '', 6);
$pdf->SetXY($columns[0]+$mid_col, (3*$edge)+($space*1));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->total_fat));
$pdf->SetXY($columns[1], (3*$edge)+($space*1));
$pdf->Cell(1, 0.10, sprintf('%d%%', round(100*$this->numericVal($this->form->total_fat) / $this->daily_values['total_fat'])));
$pdf->SetXY($columns[0], (3*$edge)+($space*2));
$pdf->Cell($columns[0], 0.10, ' Sat. Fat');
$pdf->SetXY($columns[0]+$mid_col, (3*$edge)+($space*2));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->sat_fat));
$pdf->SetXY($columns[1], (3*$edge)+($space*2));
$pdf->Cell(1, 0.10, sprintf('%d%%', round(100*$this->numericVal($this->form->sat_fat) / $this->daily_values['sat_fat'])));
$pdf->SetXY($columns[0], (3*$edge)+($space*3));
$pdf->Cell($columns[0], 0.10, ' Trans Fat');
$pdf->SetXY($columns[0]+$mid_col, (3*$edge)+($space*3));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->trans_fat));
$pdf->SetFont('Arial', 'B', 6);
$pdf->SetXY($columns[0], (3*$edge)+($space*4));
$pdf->Cell($columns[0], 0.10, 'Cholest.');
$pdf->SetFont('Arial', '', 6);
$pdf->SetXY($columns[0]+$mid_col, (3*$edge)+($space*4));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->cholest, 'mg'));
$pdf->SetXY($columns[1], (3*$edge)+($space*4));
$pdf->Cell(1, 0.10, sprintf('%d%%', round(100*$this->numericVal($this->form->cholest) / $this->daily_values['cholest'])));
$pdf->SetFont('Arial', 'B', 6);
$pdf->SetXY($columns[0], (3*$edge)+($space*5));
$pdf->Cell($columns[0], 0.10, 'Sodium');
$pdf->SetFont('Arial', '', 6);
$pdf->SetXY($columns[0]+$mid_col, (3*$edge)+($space*5));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->sodium, 'mg'));
$pdf->SetXY($columns[1], (3*$edge)+($space*5));
$pdf->Cell(1, 0.10, sprintf('%d%%', round(100*$this->numericVal($this->form->sodium) / $this->daily_values['sodium'])));
$pdf->SetFont('Arial', 'B', 6);
$pdf->SetXY($columns[2], (3*$edge)+($space*1));
$pdf->Cell($columns[0], 0.10, 'Total Carb.');
$pdf->SetFont('Arial', '', 6);
$pdf->SetXY($columns[2]+$mid_col, (3*$edge)+($space*1));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->total_carb));
$pdf->SetXY($columns[3], (3*$edge)+($space*1));
$pdf->Cell(1, 0.10, sprintf('%d%%', round(100*$this->numericVal($this->form->total_carb) / $this->daily_values['total_carb'])));
$pdf->SetXY($columns[2], (3*$edge)+($space*2));
$pdf->Cell($columns[0], 0.10, ' Fiber');
$pdf->SetXY($columns[2]+$mid_col, (3*$edge)+($space*2));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->fiber));
$pdf->SetXY($columns[3], (3*$edge)+($space*2));
$pdf->Cell(1, 0.10, sprintf('%d%%', round(100*$this->numericVal($this->form->fiber) / $this->daily_values['fiber'])));
$pdf->SetXY($columns[2], (3*$edge)+($space*4));
$pdf->Cell($columns[0], 0.10, ' Sugar');
$pdf->SetXY($columns[2]+$mid_col, (3*$edge)+($space*4));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->sugar));
$pdf->SetFont('Arial', 'B', 6);
$pdf->SetXY($columns[2], (3*$edge)+($space*5));
$pdf->Cell($columns[0], 0.10, 'Protein');
$pdf->SetFont('Arial', '', 6);
$pdf->SetXY($columns[2]+$mid_col, (3*$edge)+($space*5));
$pdf->Cell(1, 0.10, $this->normalizeVal($this->form->protein));
$pdf->SetXY($columns[3], (3*$edge)+($space*5));
$pdf->Cell(1, 0.10, sprintf('%d%%', round(100*$this->numericVal($this->form->protein) / $this->daily_values['protein'])));
$bottom = '';
for ($i=0; $i<count($this->form->nutrient); $i++) {
if ($this->form->nutrient[$i] === '') {
continue;
}
$bottom .= $this->form->nutrient[$i] . ' ';
if ($this->form->percent[$i] === '') {
$bottom .= '0% ';
} else {
$bottom .= $this->form->percent[$i] . '% ' . chr(183) . ' ';;
}
}
$bottom = rtrim($bottom, chr(183) . ' ');
$pdf->SetXY($columns[0], (2*$edge)+($space*6));
$pdf->Cell(2.45, 0.10, $bottom, 0, 0, 'C');
$outfile = tempnam(sys_get_temp_dir(), 'nfp');
$outimg = tempnam(sys_get_temp_dir(), 'nfi');
$pdf->Output($outfile, 'F');
exec("convert -density 300 \"$outfile\" \"$outimg.png\"");
unlink($outfile);
header('Content-Type: image/png');
header('Content-disposition: attachment; filename=nutritionfacts.png');
echo file_get_contents($outimg . '.png');
unlink($outimg . '.png');
return false;
}
private function normalizeVal($val, $units='g')
{
if ($val === '') {
return 0 . $units;
}
return is_numeric($val) ? $val . $units : $val;
}
private function numericVal($val, $units='g')
{
if ($val === '') {
return 0;
}
if (is_numeric($val)) {
return $val;
} else {
$val = trim($val, $units);
return trim($val);
}
}
protected function get_view()
{
$this->addScript('nutrifacts.js');
$ret = <<<HTML
<form method="post" target="_blank">
<div class="form-group form-inline">
<label>UPC</label>
<input type="text" name="upc" class="form-control input-sm" required />
</div>
<div class="form-group form-inline">
<span id="item-name" />
</div>
<table class="table table-bordered">
<tr>
<th>Serv. Size</th>
<td><input type="text" class="form-control input-sm" name="serving_size" /></td>
<th>Calories</th>
<td><input type="number" class="form-control input-sm" name="calories" /></td>
<th>Fat Calories</th>
<td><input type="number" class="form-control input-sm" name="fat_calories" /></td>
</tr>
<tr>
<th>Total Fat</th>
<td><input type="text" class="form-control input-sm" name="total_fat" /></td>
<th>Sat. Fat</th>
<td><input type="text" class="form-control input-sm" name="sat_fat" /></td>
<th>Trans Fat</th>
<td><input type="text" class="form-control input-sm" name="trans_fat" /></td>
</tr>
<tr>
<th>Cholest.</th>
<td><input type="text" class="form-control input-sm" name="cholest" /></td>
<th>Sodium</th>
<td><input type="text" class="form-control input-sm" name="sodium" /></td>
<th>Total Carb</th>
<td><input type="text" class="form-control input-sm" name="total_carb" /></td>
</tr>
<tr>
<th>Fiber</th>
<td><input type="text" class="form-control input-sm" name="fiber" /></td>
<th>Sugar</th>
<td><input type="text" class="form-control input-sm" name="sugar" /></td>
<th>Protein</th>
<td><input type="text" class="form-control input-sm" name="protein" /></td>
</tr>
HTML;
for ($i=1; $i<=4; $i++) {
$ret .= sprintf('<tr><th>Nutrient #%d</th>
<td colspan="3"><input type="text" class="nutrient-in form-control input-sm" name="nutrient[]" /></td>
<th>%%Pct</th>
<td><input type="text" class="form-control input-sm dv-in" name="percent[]" /></td>
</tr>', $i);
}
$ret .= '</table>
<p>
<button type="submit" class="btn btn-default">Get Image</button>
</p>
</form>';
return $ret;
}
public function unitTest($phpunit)
{
$phpunit->assertNotEquals(0, strlen($this->get_view()));
$phpunit->assertEquals('0g', $this->normalizeVal(''));
$phpunit->assertEquals('1g', $this->normalizeVal('1'));
$phpunit->assertEquals(0, $this->numericVal(''));
$phpunit->assertEquals(1, $this->numericVal('1'));
$phpunit->assertEquals(1, $this->numericVal('1 g'));
}
public function helpContent()
{
return '<p>Enter as many values as necessary to generate an image of a
nutrition facts box. Consider this fairly alpha</p>';
}
}
FannieDispatch::conditionalExec();