diff options
author | cos <cos> | 2015-04-06 21:58:01 +0200 |
---|---|---|
committer | cos <cos> | 2015-04-06 22:00:09 +0200 |
commit | 9f8f7ba22658ced4a728706bbd01b79e66261f1f (patch) | |
tree | 2df797a30d06ef7d4b31950a12a80a88c11fa09d | |
parent | c5315fdca11b3a32299dbcedd153c608a1e05824 (diff) | |
parent | 2390635012871d02e21d3c6c3b39860159ef862f (diff) | |
download | mat-9f8f7ba22658ced4a728706bbd01b79e66261f1f.zip |
Release 0.6.0.
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | TODO | 8 | ||||
-rwxr-xr-x | authenticate_session | 5 | ||||
-rw-r--r-- | debian/changelog | 6 | ||||
-rwxr-xr-x | mat | 440 | ||||
-rwxr-xr-x | papersize.sh | 20 | ||||
-rwxr-xr-x | sheetprint | 74 |
7 files changed, 414 insertions, 147 deletions
@@ -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". @@ -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. @@ -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"); |