summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcos <cos>2015-04-06 21:58:01 +0200
committercos <cos>2015-04-06 22:00:09 +0200
commit9f8f7ba22658ced4a728706bbd01b79e66261f1f (patch)
tree2df797a30d06ef7d4b31950a12a80a88c11fa09d
parentc5315fdca11b3a32299dbcedd153c608a1e05824 (diff)
parent2390635012871d02e21d3c6c3b39860159ef862f (diff)
downloadmat-9f8f7ba22658ced4a728706bbd01b79e66261f1f.zip
Release 0.6.0.
-rw-r--r--README8
-rw-r--r--TODO8
-rwxr-xr-xauthenticate_session5
-rw-r--r--debian/changelog6
-rwxr-xr-xmat440
-rwxr-xr-xpapersize.sh20
-rwxr-xr-xsheetprint74
7 files changed, 414 insertions, 147 deletions
diff --git a/README b/README
index d556444..c86f394 100644
--- a/README
+++ b/README
@@ -10,7 +10,13 @@ CREATE TABLE ingredients (id INTEGER PRIMARY KEY AUTOINCREMENT, name, shop_posit
CREATE TABLE contents (recipe_id INTEGER, ingredient_id INTEGER, quantity FLOAT, unit);
CREATE TABLE plan (date DATETIME, mealtype, recipe_id INTEGER, state, comment_id);
CREATE TABLE comments (id INTEGER PRIMARY KEY AUTOINCREMENT, comment);
-CREATE TABLE inventory (id INTEGER PRIMARY KEY AUTOINCREMENT, recipe_id INTEGER, preparation_date DATETIME, amount INTEGER, storage);
+CREATE TABLE inventory (id INTEGER PRIMARY KEY AUTOINCREMENT, recipe_id INTEGER, amount INTEGER, storage, preparation_date DATETIME, energy INTEGER);
+CREATE TABLE queue (id INTEGER PRIMARY KEY, recipe_id INTEGER, servings INTEGER);
+CREATE TABLE mealtypes (id INTEGER PRIMARY KEY AUTOINCREMENT, mealtype);
+CREATE TABLE typemap (recipe_id INTEGER, mealtype_id INTEGER);
+CREATE TABLE stock (product_id TEXT, quantity FLOAT, unit TEXT, up_to_date DATETIME);
+CREATE TABLE products (id TEXT PRIMARY KEY, ingredient_id INTEGER, description TEXT);
+CREATE TABLE cookings (recipe_id INTEGER, date DATETIME, specific_energy INTEGER);
All basic operations are performed using the perl script mat. It is possible to
get a crude list of commands available by running: "mat help".
diff --git a/TODO b/TODO
index c311f18..76771ff 100644
--- a/TODO
+++ b/TODO
@@ -1,12 +1,10 @@
* Replace currently cooking with a planned queue of meals.
- * Implement web thingy to view & relocate portions.
+ * Implement unit conversion for ingredients.
* Combine into one row on the shoppinglist when there are several entries of
- the same ingredient.
+ the same ingredient, even when their units differ.
- * Add command to feedback when portions were too small (still hungry/ate five
- chocolate bars after lunch) or too large (became full/lunch took two hours
- to complete).
+ * Group shoppinglist by store placement.
* Include kitchen computer scripts in this repository.
diff --git a/authenticate_session b/authenticate_session
new file mode 100755
index 0000000..8995080
--- /dev/null
+++ b/authenticate_session
@@ -0,0 +1,5 @@
+#!/bin/sh -e
+
+AUTH="'authenticated' => 'yes'"
+
+sed "s/\('_SESSION_REMOTE_ADDR'\)/$AUTH, \1/"
diff --git a/debian/changelog b/debian/changelog
index dabe7a2..6461182 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+netizen-mat (0.6.0) unstable; urgency=medium
+
+ * Release with feature/queue & feature/multimeal.
+
+ -- cos <cos> Mon, 06 Apr 2015 21:58:41 +0200
+
netizen-mat (0.5) unstable; urgency=medium
* Mainly include branch feature/web-relocate.
diff --git a/mat b/mat
index 8e86cc2..0e5c33f 100755
--- a/mat
+++ b/mat
@@ -11,13 +11,15 @@ use GD;
use GD::Barcode;
use GD::Barcode::QRcode;
use Text::Iconv;
+use utf8;
+binmode(STDOUT, 'utf8:');
tie my %Config, "Config::Simple", '/etc/mat.conf';
#use Data::Dumper;
-my $db = DBI->connect("dbi:SQLite:recipe.db", "", "", {RaiseError => 1,
- AutoCommit => 1});
+my $db = DBI->connect($Config{'database'}, "", "", {RaiseError => 1,
+ AutoCommit => 1, sqlite_unicode => 1});
my @recipes;
my @ingredients;
@@ -122,8 +124,7 @@ my @schedule = (
);
sub cmd_setmeal {
- my ( $date, $recipe_id ) = @_;
- my $mealtype = "lunch";
+ my ( $date, $mealtype, $recipe_ids ) = @_;
if ($date =~ /^Mon|Tue|Wed|Thu|Fri|Sat|Sun/) {
my $wday;
@@ -142,16 +143,54 @@ sub cmd_setmeal {
$date = $dt->ymd();
}
+ return unless $mealtype =~ "^(frukost)|(elvakaffe)|(lunch)|(fruktstund)|(middag)\$";
return unless $date =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}\$";
- return -1 unless $recipe_id =~ "^[0-9]+\$";
- if (get_recipe_name($recipe_id) ne 'NULL') {
- my $sql = "DELETE FROM plan WHERE date='$date' and mealtype='$mealtype';";
- $db->do($sql);
- $sql = "INSERT INTO plan (date, mealtype, recipe_id) VALUES ('$date',
- '$mealtype', $recipe_id);";
- $db->do($sql);
+ return -1 unless $recipe_ids =~ m/^[0-9+]+$|^-$/;
+ my $sql = "DELETE FROM plan WHERE date='$date' and mealtype='$mealtype';";
+ $db->do($sql);
+
+ for my $recipe_id (split('\+', $recipe_ids))
+ {
+ if ($recipe_id ne '-' && get_recipe_name($recipe_id) ne 'NULL') {
+ $sql = "INSERT INTO plan (date, mealtype, recipe_id) VALUES ('$date',
+ '$mealtype', $recipe_id);";
+ $db->do($sql);
+ }
+ }
+
+ return 0;
+}
+
+sub cmd_setcomment {
+ my ( $date, $mealtype, $comment ) = @_;
+
+ if ($date =~ /^Mon|Tue|Wed|Thu|Fri|Sat|Sun/) {
+ my $wday;
+ $wday = 1 if ($date =~ /^Mon/);
+ $wday = 2 if ($date =~ /^Tue/);
+ $wday = 3 if ($date =~ /^Wed/);
+ $wday = 4 if ($date =~ /^Thu/);
+ $wday = 5 if ($date =~ /^Fri/);
+ $wday = 6 if ($date =~ /^Sat/);
+ $wday = 7 if ($date =~ /^Sun/);
+
+ my $dt = DateTime->now();
+ while ($dt->wday() != $wday) {
+ $dt->add(days => 1);
+ }
+
+ $date = $dt->ymd();
}
+ return unless $mealtype =~ "^(frukost)|(elvakaffe)|(lunch)|(fruktstund)|(middag)\$";
+ return unless $date =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}\$";
+
+ my $sql = "INSERT INTO comments VALUES (NULL, '$comment');";
+ $db->do($sql);
+ my $comment_id = $db->last_insert_id(undef, undef, undef, undef);
+ $sql = "INSERT INTO plan (date, mealtype, comment_id) VALUES ('$date',
+ '$mealtype', $comment_id);";
+ $db->do($sql);
return 0;
}
@@ -200,38 +239,6 @@ sub cmd_inventory {
printf "----\n%3d\n", $total;
}
-sub cmd_setstate {
- my ( $date, $state ) = @_;
- my $mealtype = "lunch";
-
- if ($date =~ /^Mon|Tue|Wed|Thu|Fri|Sat|Sun/) {
- my $wday;
- $wday = 1 if ($date =~ /^Mon/);
- $wday = 2 if ($date =~ /^Tue/);
- $wday = 3 if ($date =~ /^Wed/);
- $wday = 4 if ($date =~ /^Thu/);
- $wday = 5 if ($date =~ /^Fri/);
- $wday = 6 if ($date =~ /^Sat/);
- $wday = 7 if ($date =~ /^Sun/);
-
- my $dt = DateTime->now();
- while ($dt->wday() != $wday) {
- $dt->add(days => 1);
- }
-
- $date = $dt->ymd();
- }
- return unless $date =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}\$";
-
- return -1 unless $state =~ "^frozen|ready|sourced|idea\$";
- if (1) { #FIXME Verify that entry exists ?
- my $sql = "UPDATE plan SET state=".$db->quote($state)." WHERE date='$date';";
- $db->do($sql);
- }
-
- return 0;
-}
-
sub cmd_randmeal {
my ( $date ) = @_;
my $recipe_id = get_random_recipe();
@@ -265,29 +272,56 @@ sub cmd_postpone {
}
sub print_label {
- my ( $id, $dish_name, $amount ) = @_;
+ my ( $id, $dish_name, $amount, $preparation_date, $energy ) = @_;
my $fontname = "/usr/share/fonts/truetype/freefont/FreeSans.ttf";
my $fontsize = 15;
- my $x_border = 40;
- my $y_border = 40;
+ my $x_border = 18; # Obtained with papersize.sh
+ my $y_border = 35;
+ my $x_limit = 324;
+ my $y_limit = 307;
+ my $x_max = $x_limit-$x_border;
+ my $y_max = $y_limit-$y_border;
+
+ my $qr_border = 4;
my $iconv = Text::Iconv->new("UTF-8", "ISO8859-1");
- my $row0 = $Config{'label_name'};
- my $row1 = $iconv->convert($dish_name);
- my $row2 = `date +%Y%m%d|tr -d '\n'`.' ('.$amount.'g)';
+ my $latin1_dish = $iconv->convert($dish_name);
+ my $row0 = $Config{'label_name'}." ".$preparation_date;
+ my $row1;
+ my $row2;
+ if( length($latin1_dish) < 15) {
+ $row1 = $latin1_dish;
+ $row2 = "(".$amount."g)";
+ } else {
+ $row1 = $latin1_dish;
+ $row1 =~ s/(.{15,23}) .*/$1/;
+ $row2 = substr($latin1_dish, (length $row1) + 1).' ('.$amount.'g)';
+ }
+ my $row3;
+ if ($energy) {
+ $row3 = $energy.' kJ';
+ } else {
+ $row3 = '<energy content unknown>';
+ }
my $idbarcode = GD::Barcode::QRcode->new(sprintf("%s%d", $Config{'label_id_prefix'}, $id), { Version=>3 });
my $idbarcode_image=$idbarcode->plot();
+ my $qr_cropped = new GD::Image($idbarcode_image->width() - 2 * $qr_border, $idbarcode_image->height() - 2 * $qr_border);
+ my $white = $qr_cropped->colorAllocate(255,255,255);
+ my $black = $qr_cropped->colorAllocate(0,0,0);
+ $qr_cropped->fill(0, 0, $white);
+ $qr_cropped->copy($idbarcode_image, 0, 0, $qr_border, $qr_border, $idbarcode_image->width() - $qr_border, $idbarcode_image->height() - $qr_border);
+
my $logo;
if ($ENV{'PRINT_WITH_LOGO'}) {
$logo = GD::Image->newFromPng("layer1.png");
}
my $idtext = new GD::Image(500, $fontsize * 2);
- my $white = $idtext->colorAllocate(255,255,255);
- my $black = $idtext->colorAllocate(0,0,0);
+ $white = $idtext->colorAllocate(255,255,255);
+ $black = $idtext->colorAllocate(0,0,0);
$idtext->fill(1, 1, $white);
$idtext->string(gdGiantFont, 0, 0, $id, $black);
@@ -297,7 +331,6 @@ sub print_label {
$text0->fill(1, 1, $white);
my @bounds0 = $text0->stringFT(-1 * $black, $fontname, $fontsize, 0, 0, 2 * $fontsize, $row0);
my $text0_image=$text0->copyRotate90();
- $text0_image->line(0, 0, 100, 100, $white);
my $text1 = new GD::Image(500, $fontsize * 3);
$white = $text1->colorAllocate(255,255,255);
@@ -315,95 +348,148 @@ sub print_label {
my @bounds2 = $text2->stringFT(-1 * $black, $fontname, $fontsize, 0, 0, 2 * $fontsize, $row2);
my $text2_image=$text2->copyRotate90();
- my $labelwidth = $idbarcode_image->width() * 5 + $idtext->height() * 2 + 2 * $y_border;
- if($bounds0[2] + 2 * $y_border > $labelwidth) {
- $labelwidth = $bounds0[2] + 2 * $y_border;
- }
- if($bounds1[2] + 2 * $y_border > $labelwidth) {
- $labelwidth = $bounds1[2] + 2 * $y_border;
- }
- if($bounds2[2] + 2 * $y_border > $labelwidth) {
- $labelwidth = $bounds2[2] + 2 * $y_border;
- }
- if($labelwidth > 1063)
- {
- $labelwidth = 1063;
- }
-
- # 300 dpi & 29 x 90 mm ger: 300*2.9/2.54 = 343, 300*9.0/2.54 = 1063
- my $label = new GD::Image(343, $labelwidth);
+ my $text3 = new GD::Image(500, $fontsize * 3);
+ $white = $text3->colorAllocate(255,255,255);
+ $black = $text3->colorAllocate(0,0,0);
+ $text3->fill(1, 1, $white);
+ $text3->filledRectangle(0, 0, $text3->width(), $text3->height(), $white);
+ my @bounds3 = $text3->stringFT(-1 * $black, $fontname, $fontsize, 0, 0, 2 * $fontsize, $row3);
+ my $text3_image=$text3->copyRotate90();
+ my $label = new GD::Image($x_max, $y_max);
$white = $label->colorAllocate(255,255,255);
$black = $label->colorAllocate(0,0,0);
$label->fill(0, 0, $white);
- $label->copyResized($idbarcode_image, $x_border, $y_border, 0, 0, $idbarcode_image->width() * 5, $idbarcode_image->height() * 5, $idbarcode_image->width(), $idbarcode_image->height());
- $label->copyResized($idtext, $x_border * 3, $y_border + $idbarcode_image->height() * 5, 0, 0, $idtext->width() * 2, $idtext->height() * 2, $idtext->width(), $idtext->height());
- $label->copyResized($text0_image, $label->width() - $x_border - 2 * $fontsize, $y_border, 0, 0, $bounds0[1] * 1, $bounds0[2], $bounds0[1], $bounds0[2]);
- $label->copyResized($text1_image, $label->width() - $x_border - 4 * $fontsize, $y_border, 0, 0, $bounds1[1] * 1, $bounds1[2], $bounds1[1], $bounds1[2]);
- $label->copyResized($text2_image, $label->width() - $x_border - 6 * $fontsize, $y_border, 0, 0, $bounds2[1] * 1, $bounds2[2], $bounds2[1], $bounds2[2]);
+ $label->copyResized($qr_cropped, 0, 0, 0, 0, $qr_cropped->width() * 5, $qr_cropped->height() * 5, $qr_cropped->width(), $qr_cropped->height());
+ $label->copyResized($idtext, 0, 0 + $qr_cropped->height() * 5, 0, 0, $idtext->width() * 2, $idtext->height() * 2, $idtext->width(), $idtext->height());
+ $label->copyResized($text0_image, $x_max - 2 * $fontsize, 0, 0, 0, $bounds0[1] * 1, $bounds0[2], $bounds0[1], $bounds0[2]);
+ $label->copyResized($text1_image, $x_max - 4 * $fontsize, 0, 0, 0, $bounds1[1] * 1, $bounds1[2], $bounds1[1], $bounds1[2]);
+ $label->copyResized($text2_image, $x_max - 6 * $fontsize, 0, 0, 0, $bounds2[1] * 1, $bounds2[2], $bounds2[1], $bounds2[2]);
+ $label->copyResized($text3_image, $x_max - 8 * $fontsize, 0, 0, 0, $bounds3[1] * 1, $bounds3[2], $bounds3[1], $bounds3[2]);
+
if ($ENV{'PRINT_WITH_LOGO'}) {
my ($logo_w, $logo_h ) = $logo->getBounds();
my $vl = $logo->copyRotate90();
- $label->copy($vl, $x_border - 20, $label->height - $y_border + 20 - $logo_w, 0, 0, $logo_h, $logo_w);
+# $label->copy($vl, 0, $y_max - $logo_w, 0, 0, $logo_h, $logo_w);
+ $label->copy($vl, 0, 267 - $logo_w, 0, 0, $logo_h, $logo_w);
}
+ my $offset = new GD::Image($x_max, 267);
+ $white = $offset->colorAllocate(255,255,255);
+ $black = $offset->colorAllocate(0,0,0);
+ $offset->fill(0, 0, $white);
+ $offset->copy($label, 0, 0, 0, 0, $label->width(), $label->height());
+
open(PNGFILE, ">label.png");
- print PNGFILE $label->png;
+ print PNGFILE $offset->png;
system($Config{'print_command'});
}
sub cmd_storeportion {
my ( $recipe_id, $amount, $storage ) = @_;
- my $sql = "INSERT INTO inventory (recipe_id, preparation_date, amount, ".
- "storage ) VALUES ($recipe_id, ".`date +%Y%m%d|tr -d '\n'`.", $amount, ".
- "'$storage' );";
+ my $energy;
+ my $sql = "SELECT * FROM cookings WHERE recipe_id=$recipe_id AND ".
+ "julianday('now')-julianday(date) < 2;";
+ my $row = $db->selectrow_hashref($sql);
+ if ($row) {
+ $energy = int($amount * $row->{'specific_energy'} / 100);
+ }
+
+ $sql = "INSERT INTO inventory (recipe_id, preparation_date, amount, ".
+ "storage, energy ) VALUES ($recipe_id, ".`date +%Y%m%d|tr -d '\n'`.",
+ $amount, '$storage', ".(defined($energy) ? "$energy" : 'NULL').");";
$db->do($sql);
my $inventory_id = $db->last_insert_id(undef, undef, undef, undef);
- print_label($inventory_id, get_recipe_name($recipe_id), $amount);
+ print_label($inventory_id, get_recipe_name($recipe_id), $amount,
+ `date +%Y%m%d|tr -d '\n'`, $energy);
}
-sub get_plan_state {
- my ( $date ) = @_;
- my $sql = "SELECT state FROM plan WHERE date=".$db->quote($date).";";
- my ( $state ) = $db->selectrow_array($sql);
- $state = 'u' unless $state;
+sub cmd_reprintlabel {
+ my ( $id ) = @_;
+
+ my $sql = "SELECT recipe_id, preparation_date, amount, storage, energy FROM ".
+ "inventory WHERE id=$id";
+
+ my $row = $db->selectrow_hashref($sql);
+ print_label($id, get_recipe_name($row->{'recipe_id'}), $row->{'amount'},
+ $row->{'preparation_date'}, $row->{'energy'});
+
+ return 0;
+}
+
+sub get_plan_state {
+ my ( $recipe_id, $end_date ) = @_;
+ my $state;
+
+ my $sql = "SELECT COUNT(id) FROM inventory WHERE recipe_id=$recipe_id AND ".
+ "(storage GLOB '*frys' OR storage GLOB '*kyl');";
+ my $prepared = $db->selectcol_arrayref($sql);
+
+ $sql = "SELECT recipe_id FROM plan WHERE recipe_id=$recipe_id AND ".
+ "date >= '".DateTime->now->ymd()."' AND date < '$end_date';";
+ my $already_planned = $db->selectcol_arrayref($sql);
+ my $portions_left = @{$prepared}[0] - scalar(@{$already_planned});
+ if ( $portions_left > 0 ) {
+ $state = 'prepared';
+ } else {
+ $state = 'unavailable';
+ }
return $state;
}
sub cmd_showplan {
- my ( $date ) = @_;
+ my ( $days, $date ) = @_;
my $dt;
my $weekend_padding = "";
+ $days = 7 unless defined($days) && $days =~ /^[0-9]{1,2}$/;
+
+ DateTime->DefaultLocale("sv_SE"); # FIXME Don't hårdkoda svenska, tack!
if($date) {
- return -1 unless $date =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}\$";
+ return -1 unless $date =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
$dt = DateTime::Format::ISO8601->parse_datetime( $date );
} else {
$dt = DateTime->now();
- # FIXME now() is not start of day, set hour, minute and such to 0
}
- for (my $i = 0; $i < 14; $i++) {
- my $sql = "SELECT recipe_id, mealtype, comment_id FROM plan WHERE date='".
- $dt->ymd()."';";
- my @ids = $db->selectrow_array($sql);
-
+ for (my $i = 0; $i < $days; $i++) {
+ my $sql = "SELECT recipe_id, p.mealtype, comment_id FROM plan AS p JOIN ".
+ "mealtypes AS m ON p.mealtype=m.mealtype WHERE date='".$dt->ymd().
+ "' ORDER by m.id;";
+ my $plan = $db->selectall_arrayref($sql);
print $weekend_padding;
- if ($ids[0]||$ids[2]) {
- printf "%9s %-10s %3s|%s %s%s%s%s\n", $dt->ymd(), $ids[1],
- (defined($ids[0]) ? $ids[0] : ""),
- (defined($ids[0]) ? substr(get_plan_state($dt->ymd()), 0, 1) : " "),
- (defined($ids[0]) ? get_recipe_name($ids[0]).", " : ""),
- (defined($ids[0]) ? get_recipe_uri($ids[0]) : ""),
- (defined($ids[0]) && defined($ids[2]) ? ", " : ""),
- (defined($ids[2]) ? get_comment($ids[2]) : "");
- } else {
- printf "%-24s | \n", $dt->ymd();
+ my $last_mealtype = "";
+ for my $meal ( @$plan ) {
+ my $mealtype = $meal->[1];
+ if ( $mealtype ne $last_mealtype ) {
+ print "\n";
+ if ($meal->[0]) {
+ printf "%-10s %-10s %3s|%s (%s)", ($last_mealtype eq "" ? $dt->ymd() :
+ ($mealtype eq "elvakaffe" ? $dt->day_name() : "")), # FIXME remove hard coded mealtype
+ $mealtype,
+ ($meal->[0] ? substr(get_plan_state($meal->[0], $dt->ymd()), 0, 1) : " "),
+ get_recipe_name($meal->[0]),
+ $meal->[0],
+ } elsif ($meal->[2]) {
+ printf "%-10s %-13s |%s",
+ ($last_mealtype eq "" ? $dt->ymd() :
+ ($mealtype eq "elvakaffe" ? $dt->day_name() : "")), $mealtype, # FIXME remove hard coded mealtype
+ get_comment($meal->[2]);
+ } else {
+ printf "%-10s %-13s | ",
+ ($last_mealtype eq "" ? $dt->ymd() :
+ ($mealtype eq "elvakaffe" ? $dt->day_name() : "")), $mealtype; # FIXME remove hard coded mealtype
+ }
+ } else {
+ print " + ", get_recipe_name($meal->[0]), " (", $meal->[0], ")";
+ }
+ $last_mealtype = $mealtype;
}
+ print "\n";
$dt->add(days => 1);
if($dt->day_of_week == 1) {
$weekend_padding = "\n";
@@ -436,16 +522,21 @@ sub cmd_help() {
print "editrecipe <recipe_id>\n";
print "showrecipe <recipe_id>\n";
print "movemeal <source_date> <destination_date>\n";
- print "setmeal <date> <meal_id>\n";
- print "setstate <date> <idea|sourced|frozen|ready>\n";
+ print "setmeal <date> <mealtype> <meal_id>\n";
+ print "setcomment <date> <mealtype> <comment>\n";
print "randmeal <date>\n";
- print "showplan [date]\n";
+ print "showplan [days] [date]\n";
print "postpone <date> <gap>\n";
print "storeportion <recipe_id> [amount] [storage]\n";
+ print "reprintlabel <inventory_id>\n";
print "retrieveportion <portion_id> (unimplemented)\n";
print "relocate <portion_id> <new_storage>\n";
print "inventory [storage]\n";
print "shoppinglist <days_to_shop_for>\n";
+ print "queueshow\n";
+ print "queueadd <recipe_id> <servings>\n";
+ print "queuerm <queue_id>\n";
+ print "queuemv (Yet to be implemented)\n";
}
sub interactive_edit_recipe_ingredients {
@@ -610,6 +701,58 @@ sub cmd_searchrecipes {
return 1;
}
+sub cmd_queueadd {
+ my ( $recipe_id, $servings ) = @_;
+
+ my $sql = "SELECT COUNT(id) FROM queue;";
+ my $id = $db->selectrow_arrayref($sql);
+
+ return -1 unless $recipe_id =~ "^[0-9]+\$";
+ return -1 unless $servings =~ "^[0-9]+\$";
+ if (get_recipe_name($recipe_id) ne 'NULL') {
+ $sql = "INSERT INTO queue (id, recipe_id, servings) VALUES (@$id[0], ".
+ "$recipe_id, $servings)";
+ $db->do($sql);
+ }
+
+ return 0;
+}
+
+sub cmd_queueshow {
+ my $sql = "SELECT id, recipe_id, servings FROM queue";
+
+ my $sth = $db->prepare($sql);
+ my $rv = $sth->execute;
+ $db->{RaiseError} = 0;
+ while (my @row_ary = $sth->fetchrow_array) {
+ printf "%3s|%2d x %s (%d)\n", $row_ary[0], $row_ary[2],
+ get_recipe_name($row_ary[1]), $row_ary[1];
+
+ }
+ $db->{RaiseError} = 1;
+}
+
+sub cmd_queuerm {
+ my ( $queue_id ) = @_;
+
+ my $sql = "DELETE FROM queue WHERE id=$queue_id;";
+ my $sth = $db->prepare($sql);
+ my $rv = $sth->execute;
+
+ my $id = $queue_id;
+ do {
+ my $next = $id + 1;
+ $sql = "UPDATE queue SET id=$id WHERE id=$next;";
+
+ $id++;
+ } while($db->do($sql) == 1);
+}
+
+sub cmd_queuemv {
+ print "Yet to be implemented!\n";
+}
+
+
sub cmd_shoppinglist {
# Argument is number of days to shop for
@@ -620,34 +763,20 @@ sub cmd_shoppinglist {
$enddate->add(days => $shopdays);
-# First: Create a list of all meals from now to now+shopdays
-# Second: Divide each meal type with the ration size and apply the roof function
-# Third: Generate a list of all ingredients
-# Voila!
-
-# for (my $i = 0; $i < $shopdays; $i++) {
-# # FIXME Skip ingredients for ready or frozen recipes
-# print $i, "\n";
-# }
-
- my $entries = $db->selectall_arrayref("SELECT recipe_id FROM plan WHERE recipe_id AND date ".
- "BETWEEN '".$startdate."' AND '".$enddate."' AND ".
- "IFNULL(state, 'null') != 'frozen' AND ".
- "IFNULL(state, 'null') != 'ready' AND ".
- "IFNULL(state, 'null') != 'sourced'", { Slice => {} });
+ my $plan_entries = $db->selectall_arrayref("SELECT recipe_id, date FROM plan WHERE recipe_id AND date ".
+ "BETWEEN '".$startdate."' AND '".$enddate."';", { Slice => {} });
+ my $queue_entries = $db->selectall_arrayref("SELECT recipe_id FROM queue;");
my %recipe_count;
- for my $entry ( @$entries ) {
- $recipe_count{$entry->{recipe_id}}++;
-# print $recipe_count{$entry->{recipe_id}}, " ", $entry->{recipe_id}, "\n";
-## print $entry->{recipe_id}."";
-## print " count: ", scalar(grep /$entry->{recipe_id}/, @$entries), " ";
-## print " count: ", $count{31}, "\n";
-## print " count: ", $count{$entry->{recipe_id}}, "\n";
-## # FIXME Take number of servings into account
-## TODO Loop through recipes to add ingredients to a list
-## TODO Print ingredients
- }
-# map {print "| $_ = ${recipe_count{$_}}\n"} sort keys(%recipe_count);
+ for my $entry ( @$plan_entries ) {
+ if (get_plan_state($entry->{'recipe_id'}, $entry->{'date'}) ne "prepared") {
+ $recipe_count{$entry->{recipe_id}}++;
+ }
+ }
+ for my $entry ( @$queue_entries ) {
+ my @s = $db->selectrow_array("SELECT servings FROM queue WHERE ".
+ "recipe_id=@$entry[0]");
+ $recipe_count{@$entry[0]} += $s[0];
+ }
my @shop_recipes;
for my $recipe ( keys(%recipe_count) )
@@ -661,8 +790,6 @@ sub cmd_shoppinglist {
$servings = 1;
print "WARNING servings is not set for recipe $recipe!\n";
}
-# print "> $recipe", " ", $recipe_count{$recipe}, " ", $servings, "\n";
-# print "| $recipe", " ", ceiling($recipe_count{$recipe}/$servings), "\n";
my $cookings = ceiling($recipe_count{$recipe}/$servings);
for (my $i=0; $i < $cookings; $i++) {
@@ -689,14 +816,33 @@ sub cmd_shoppinglist {
# $shop{shop_position} =
$shop{unit} = $$contents{$content}{unit};
$shop{ingredient} = @$ingredientcol[0];
+ $shop{recipe}[0] = $recipe;
push @shop_ingredients, \%shop;
}
}
my @sorted_ingredients = sort { $a->{ingredient} cmp $b->{ingredient} } @shop_ingredients;
- for my $shop ( @sorted_ingredients )
+ my @squeezed_ingredients;
+ for my $squeeze ( @sorted_ingredients )
+ {
+ if (exists($squeezed_ingredients[-1]) and $squeezed_ingredients[-1]{ingredient} eq $squeeze->{ingredient} and $squeezed_ingredients[-1]{unit} eq $squeeze->{unit}) {
+ $squeezed_ingredients[-1]{quantity} += $squeeze->{quantity};
+ push @{$squeezed_ingredients[-1]{recipe}}, $squeeze->{recipe}[0] unless grep(/$squeeze->{recipe}[0]/, @{$squeezed_ingredients[-1]{recipe}});
+ } else {
+ push @squeezed_ingredients, $squeeze;
+ }
+ }
+# for my $shop ( @sorted_ingredients )
+ for my $shop ( @squeezed_ingredients )
{
- printf "%4s %-8s %s\n", $shop->{quantity}, $shop->{unit}, $shop->{ingredient};
+ printf "%4s %-8s %-40s", $shop->{quantity}, $shop->{unit}, $shop->{ingredient};
+ for ( @{$shop->{recipe}} ) {
+ print get_recipe_name($_), ", ";
+ }
+ print "\n";
}
+ open(my $f, "extrashopping.txt");
+ my $extra = <$f>;
+ print "$extra";
return 1;
}
@@ -718,21 +864,33 @@ if ($ARGV[0]) {
} elsif ( $ARGV[0] eq "movemeal") {
print "Command failed!\n" unless (cmd_movemeal($ARGV[1], $ARGV[2]) >= 0);
} elsif ( $ARGV[0] eq "setmeal") {
- print "Command failed!\n" unless (cmd_setmeal($ARGV[1], $ARGV[2]) >= 0);
- } elsif ( $ARGV[0] eq "setstate") {
- print "Command failed!\n" unless (cmd_setstate($ARGV[1], $ARGV[2]) >= 0);
+ print "Command failed!\n" unless (cmd_setmeal($ARGV[1], $ARGV[2], $ARGV[3]) >= 0);
+ } elsif ( $ARGV[0] eq "setcomment") {
+ print "Command failed!\n" unless (cmd_setcomment($ARGV[1], $ARGV[2], $ARGV[3]) >= 0);
} elsif ( $ARGV[0] eq "randmeal") {
print "Command failed!\n" unless (cmd_randmeal($ARGV[1]) >= 0);
} elsif ( $ARGV[0] eq "showplan") {
- print "Command failed!\n" unless (cmd_showplan($ARGV[1]) >= 0);
+ print "Command failed!\n" unless (cmd_showplan($ARGV[1], $ARGV[2]) >= 0);
} elsif ( $ARGV[0] eq "postpone") {
print "Command failed!\n" unless (cmd_postpone($ARGV[1]) >= 0);
} elsif ( $ARGV[0] eq "inventory") {
print "Command failed!\n" unless (cmd_inventory($ARGV[1]) >= 0);
} elsif ( $ARGV[0] eq "storeportion") {
print "Command failed!\n" unless (cmd_storeportion($ARGV[1], $ARGV[2], $ARGV[3]) >= 0);
+ } elsif ( $ARGV[0] eq "labb") {
+ print "Command failed!\n" unless (cmd_labb($ARGV[1], $ARGV[2], $ARGV[3]) >= 0);
+ } elsif ( $ARGV[0] eq "reprintlabel") {
+ print "Command failed!\n" unless (cmd_reprintlabel($ARGV[1]) >= 0);
} elsif ( $ARGV[0] eq "relocate") {
print "Command failed!\n" unless (cmd_relocate($ARGV[1], $ARGV[2]) >= 0);
+ } elsif ( $ARGV[0] eq "queueadd") {
+ print "Command failed!\n" unless (cmd_queueadd($ARGV[1], $ARGV[2]) >= 0);
+ } elsif ( $ARGV[0] eq "queuerm") {
+ print "Command failed!\n" unless (cmd_queuerm($ARGV[1], $ARGV[2]) >= 0);
+ } elsif ( $ARGV[0] eq "queuemv") {
+ print "Command failed!\n" unless (cmd_queuemv($ARGV[1], $ARGV[2]) >= 0);
+ } elsif ( $ARGV[0] eq "queueshow") {
+ print "Command failed!\n" unless (cmd_queueshow());
} elsif ( $ARGV[0] eq "shoppinglist") {
print "Command failed!\n" unless (cmd_shoppinglist($ARGV[1]) >= 0);
} else {
diff --git a/papersize.sh b/papersize.sh
new file mode 100755
index 0000000..a05b396
--- /dev/null
+++ b/papersize.sh
@@ -0,0 +1,20 @@
+#!/bin/zsh -e
+
+PAPERSIZE=`lpoptions -p lbla -l|sed --silent 's/^PageSize.*\*\([^ ]*\).*/\1/p'`
+FULLNAME=`rgrep "$PAPERSIZE" /etc/cups 2>/dev/null|
+ sed --silent 's/.*PaperDimension\(.*\):.*["]\([0-9.[:blank:]]*\)"/\1/p'`
+DIMENSIONS=`rgrep "$PAPERSIZE" /etc/cups 2>/dev/null|
+ sed --silent 's/.*PaperDimension.*["]\([0-9.[:blank:]]*\)"/\1/p'`
+MARGINS=`rgrep "$PAPERSIZE" /etc/cups 2>/dev/null|
+ sed --silent 's/.*ImageableArea.*["]\([0-9.[:blank:]]*\)"/\1/p'`
+
+
+echo "Papersize: $PAPERSIZE"
+echo "Fullname: $FULLNAME"
+echo "Dimensions in postscript points: $DIMENSIONS"
+echo "Margins in postscript points: $MARGINS"
+
+for value in `echo $DIMENSIONS $MARGINS`
+do
+ echo "$value pt = $value pt * 300 dpi/72 pt/inch = $(( $value * 300 / 72 )) dots"
+done
diff --git a/sheetprint b/sheetprint
new file mode 100755
index 0000000..f0052f5
--- /dev/null
+++ b/sheetprint
@@ -0,0 +1,74 @@
+#!/usr/bin/perl
+#
+# Sometimes it is handy to print an a4 sheet of labels through a regular
+# printer, rather than using a dedicated label printer. This script kind of
+# makes it possible.
+#
+# Add something like this line to /etc/mat.conf first:
+# print_command "mv /usr/local/mat/label.png /usr/local/mat/label-`date +%s`.png"
+
+use strict;
+use warnings;
+
+use GD;
+
+use constant DPI => 300;
+
+sub mm2dots
+{
+ my ( $mm ) = @_;
+
+ return ($mm/2.54/10*DPI);
+}
+
+my $a4 = new GD::Image(210/2.54/10*DPI, 297/2.54/10*DPI);
+my $white = $a4->colorAllocate(255,255,255);
+my $black = $a4->colorAllocate(0,0,0);
+$a4->fill(0, 0, $white);
+
+# Draw a cross
+$a4->line(0, 0, $a4->width(), $a4->height, $black);
+$a4->line($a4->width(), 0, 0, $a4->height, $black);
+
+# Draw some rulers
+for ( my $i = 1; $i < 297; $i++) {
+ if ( $i % 10 == 0 ) {
+ $a4->line(0, mm2dots($i), 20, mm2dots($i), $black);
+ $a4->line(mm2dots(105), mm2dots($i), mm2dots(105) + 20, mm2dots($i), $black);
+ } elsif ( $i % 5 == 0 ) {
+ $a4->line(0, mm2dots($i), 15, mm2dots($i), $black);
+ $a4->line(mm2dots(105), mm2dots($i), mm2dots(105) + 15, mm2dots($i), $black);
+ } else {
+ $a4->line(0, mm2dots($i), 10, mm2dots($i), $black);
+ $a4->line(mm2dots(105), mm2dots($i), mm2dots(105) + 10, mm2dots($i), $black);
+ }
+}
+for ( my $i = 1; $i < 210; $i++) {
+ if ( $i % 10 == 0 ) {
+ $a4->line(mm2dots($i), mm2dots(297), mm2dots($i), mm2dots(297) - 20, $black);
+ $a4->line(mm2dots($i), mm2dots(149), mm2dots($i), mm2dots(149) - 20, $black);
+ } elsif ( $i % 5 == 0 ) {
+ $a4->line(mm2dots($i), mm2dots(297), mm2dots($i), mm2dots(297) - 15, $black);
+ $a4->line(mm2dots($i), mm2dots(149), mm2dots($i), mm2dots(149) - 15, $black);
+ } else {
+ $a4->line(mm2dots($i), mm2dots(297), mm2dots($i), mm2dots(297) - 10, $black);
+ $a4->line(mm2dots($i), mm2dots(149), mm2dots($i), mm2dots(149) - 10, $black);
+ }
+}
+
+my $skip = 2;
+my $x = mm2dots(25);
+my $y = mm2dots(32) + $skip * mm2dots(25);
+
+for my $filename ( `ls label-??????????.png` ) {
+ my $label = GD::Image->newFromPng($filename);
+ $a4->copy($label, $x, $y, 0, 0, $label->width(), $label->height());
+ $y += mm2dots(25);
+}
+
+open(PNGFILE, ">a4.png");
+print PNGFILE $a4->png;
+
+my $density = DPI * 1/2.54;
+
+system("convert -density $density -define pdf:fit-page=A4 a4.png a4.pdf");