#!/usr/bin/perl use warnings; use strict; use Config::Simple; use DBI; use DateTime; use DateTime::Format::ISO8601; 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($Config{'database'}, "", "", {RaiseError => 1, AutoCommit => 1, sqlite_unicode => 1}); my @recipes; my @ingredients; my @contents; my @comments; sub ceiling { my ( $num ) = @_; my $ret = int($num); $ret++ unless (int($num) eq $num); return $ret; } sub read_ingredients { @ingredients = (); my $all = $db->selectall_arrayref("SELECT * FROM ingredients"); foreach my $row (@$all) { my ($id) = @$row; $ingredients[$id]->{'id'} = @$row[0]; $ingredients[$id]->{'name'} = @$row[1]; } } sub read_recipe_db { # All recipes needs to be read to be able to pick random recipes easily # enough. my $all = $db->selectall_arrayref("SELECT * FROM recipes"); foreach my $row (@$all) { my ($id) = @$row; $recipes[$id]->{'name'} = @$row[1]; $recipes[$id]->{'uri'} = @$row[2]; } $all = $db->selectall_arrayref("SELECT * FROM comments"); foreach my $row (@$all) { my ($id) = @$row; $comments[$id]->{'comment'} = @$row[1]; } read_ingredients; $all = $db->selectall_arrayref("SELECT * FROM contents"); foreach my $row (@$all) { my ($id) = @$row; $contents[$id]->{'recipe_id'} = @$row[1]; $contents[$id]->{'ingredient_id'} = @$row[2]; $contents[$id]->{'quantity'} = @$row[2]; $contents[$id]->{'unit'} = @$row[2]; } } sub get_random_recipe { return int(rand(@recipes)); } sub get_recipe_name ($) { my ( $id ) = @_; if ($recipes[$id]->{'name'}) { return $recipes[$id]->{'name'}; } else { return "NULL"; } } sub get_recipe_uri ($) { my ( $id ) = @_; if ($recipes[$id]->{'uri'}) { return $recipes[$id]->{'uri'}; } else { return "NULL"; } } sub get_comment ($) { my ( $id ) = @_; if ($comments[$id]->{'comment'}) { return $comments[$id]->{'comment'}; } else { return "NULL"; } } my @schedule = ( { day => "Måndag", }, { day => "Tisdag", }, { day => "Onsdag", }, { day => "Torsdag", }, { day => "Fredag", } ); sub cmd_setmeal { my ( $date, $recipe_id ) = @_; 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 $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 0; } sub cmd_movemeal { my ( $source, $destination ) = @_; my $mealtype = "lunch"; return unless $source =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}\$"; return unless $destination =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}\$"; my $sql = "SELECT FROM plan WHERE date='$source';"; # FIXME Run and validate existance of source $db->do($sql); # FIXME Handle if destination already exists $sql = "UPDATE plan SET date='$destination' WHERE date='$source';"; $db->do($sql); return 0; } sub cmd_relocate { my ( $inventory_id, $storage ) = @_; my $sql = "UPDATE inventory SET storage='$storage' WHERE id=$inventory_id;"; $db->do($sql); } sub cmd_inventory { my ( $storage ) = @_; my $total = 0; my $sql = "SELECT recipe_id, count(recipe_id), storage FROM inventory"; $sql .= " WHERE storage='$storage'" if ( $storage ); $sql .= " GROUP BY storage, preparation_date, recipe_id"; my $all = $db->selectall_arrayref($sql); foreach my $row (@$all) { $total += @$row[1]; printf "%3d x %s (%s)", @$row[1], get_recipe_name(@$row[0]), @$row[0]; if ( $storage ) { print "\n"; } else { print " ", @$row[2], "\n"; } } 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(); return cmd_setmeal($date, $recipe_id); } sub cmd_postpone { my ( $date, $gap ) = @_; my $mealtype = "lunch"; my $startdate; if($date) { return -1 unless $date =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}\$"; $startdate = DateTime::Format::ISO8601->parse_datetime( $date ); } else { $startdate = DateTime->now(); # FIXME now() is not start of day, set hour, minute and such to 0 } $gap = 1; my $entries = $db->selectall_arrayref("SELECT date FROM plan WHERE date ". "BETWEEN '".$startdate."' and '2999-01-01' ORDER BY date DESC", { Slice => {} }); for my $entry ( @$entries ) { my $nextdate = DateTime::Format::ISO8601->parse_datetime($entry->{date}); $nextdate->add(days => $gap); $db->do("UPDATE plan SET date='".$nextdate->ymd()."' WHERE date='". $entry->{date}."';\n"); } } sub print_label { my ( $id, $dish_name, $amount ) = @_; my $fontname = "/usr/share/fonts/truetype/freefont/FreeSans.ttf"; my $fontsize = 15; 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 $latin1_dish = $iconv->convert($dish_name); my $row0 = $Config{'label_name'}; my $row1; my $row2; if( length($latin1_dish) < 15) { $row1 = $latin1_dish; $row2 = ""; } else { $row1 = $latin1_dish; $row1 =~ s/(.{15,23}) .*/$1/; $row2 = substr($latin1_dish, (length $row1) + 1); } my $row3 = `date +%Y%m%d|tr -d '\n'`.' ('.$amount.'g)'; 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); $white = $idtext->colorAllocate(255,255,255); $black = $idtext->colorAllocate(0,0,0); $idtext->fill(1, 1, $white); $idtext->string(gdGiantFont, 0, 0, $id, $black); my $text0 = new GD::Image(500, $fontsize * 3); $white = $text0->colorAllocate(255,255,255); $black = $text0->colorAllocate(0,0,0); $text0->fill(1, 1, $white); my @bounds0 = $text0->stringFT(-1 * $black, $fontname, $fontsize, 0, 0, 2 * $fontsize, $row0); my $text0_image=$text0->copyRotate90(); my $text1 = new GD::Image(500, $fontsize * 3); $white = $text1->colorAllocate(255,255,255); $black = $text1->colorAllocate(0,0,0); $text1->fill(1, 1, $white); $text1->filledRectangle(0, 0, $text1->width(), $text1->height(), $white); my @bounds1 = $text1->stringFT(-1 * $black, $fontname, $fontsize, 0, 0, 2 * $fontsize, $row1); my $text1_image=$text1->copyRotate90(); my $text2 = new GD::Image(500, $fontsize * 3); $white = $text2->colorAllocate(255,255,255); $black = $text2->colorAllocate(0,0,0); $text2->fill(1, 1, $white); $text2->filledRectangle(0, 0, $text2->width(), $text2->height(), $white); my @bounds2 = $text2->stringFT(-1 * $black, $fontname, $fontsize, 0, 0, 2 * $fontsize, $row2); my $text2_image=$text2->copyRotate90(); 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($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, 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 $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' );"; $db->do($sql); my $inventory_id = $db->last_insert_id(undef, undef, undef, undef); print_label($inventory_id, get_recipe_name($recipe_id), $amount); } 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; return $state; } sub cmd_showplan { my ( $date ) = @_; my $dt; my $weekend_padding = ""; if($date) { 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); 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(); } $dt->add(days => 1); if($dt->day_of_week == 1) { $weekend_padding = "\n"; } else { $weekend_padding = ""; } } } sub cmd_randweek { # for (my $i = 0; $i < 5; $i++) { # $schedule[$i]->{'recipe'} = get_random_recipe; # } # # for (my $i = 0; $i < 5; $i++) { # print $schedule[$i]->{'day'}.": "; # print get_recipe_name($schedule[$i]->{'recipe'})."\n"; # my $contents = $db->selectall_arrayref("SELECT * FROM contents WHERE ". # "recipe_id=".$schedule[$i]->{'recipe'}); # unless (@$contents) { # print "OBSERVERA: Ingredienslista saknas för denna rätt!\n\n"; # } # } } sub cmd_help() { print "help\n"; print "addrecipe\n"; print "searchrecipes \n"; print "editrecipe \n"; print "showrecipe \n"; print "movemeal \n"; print "setmeal \n"; print "setstate \n"; print "randmeal \n"; print "showplan [date]\n"; print "postpone \n"; print "storeportion [amount] [storage]\n"; print "retrieveportion (unimplemented)\n"; print "relocate \n"; print "inventory [storage]\n"; print "shoppinglist \n"; print "queueshow\n"; print "queueadd \n"; print "queuerm \n"; print "queuemv (Yet to be implemented)\n"; } sub interactive_edit_recipe_ingredients { # TODO It would be nice to be able to input ingredients in the format: # my ( $recipe_id ) = @_; my ( $ingredient, $quantity, $unit, $answer, $sql ); while (1) { my @matched_ingredients; print "Ingredient: "; $ingredient = ; chomp $ingredient; last if ($ingredient eq ''); for (@ingredients) { if (grep (/$ingredient/, $_->{'name'})) { print $_->{'name'}, "\n"; push @matched_ingredients, $_->{'id'}; } } if (length(@matched_ingredients == 0)) { $sql = "INSERT INTO ingredients (name) VALUES ('$ingredient');"; print "$sql\n\n"; print "Add new ingredient? (y/n): "; $answer = ; if ($answer eq "y\n") { $db->do($sql); read_ingredients; } } elsif (length(@matched_ingredients == 1)) { my $ingredient_id = $matched_ingredients[0]; print "Amount: "; $quantity = ; chomp $quantity; # my $default_unit = "SELECT unit FROM contents WHERE ingredient_id='" # .$ingredient_id."' LIMIT 1;" print "Unit: "; $unit = ; chomp $unit; if (($quantity ne '') and ($unit ne '')) { $sql = "INSERT INTO contents (recipe_id, ingredient_id, quantity, ". "unit) VALUES ('$recipe_id', '$ingredient_id', '$quantity', ". "'$unit')"; print "$sql\n\n"; print "Add row? (y/n): "; $answer = ; $db->do($sql) if ($answer eq "y\n"); } } } return 0; } sub cmd_editrecipe { my ( $recipe_id ) = @_; my ( $recipe_name, $recipe_uri, $answer, $sql ); return -1 unless($recipe_id and ($recipe_name = get_recipe_name($recipe_id)) ne 'NULL'); $recipe_uri = get_recipe_uri($recipe_id); print "Recipe name: $recipe_name\n"; print "Recipe uri: $recipe_uri\n"; print "Change these? (y/n) "; $answer = ; if ($answer eq "y\n") { print "Recipe name: "; $recipe_name = ; chomp $recipe_name; print "E.g. http://stupid.domain.name/node/755, urn:isbn:9789127118348#102\n"; print "Recipe uri: "; $recipe_uri = ; chomp $recipe_uri; $sql = "UPDATE recipes SET name='$recipe_name', uri='$recipe_uri' WHERE id=$recipe_id;"; print "$sql\n\n"; print "Update database? (y/n): "; $answer = ; if ($answer eq "y\n") { $db->do($sql); } } interactive_edit_recipe_ingredients($recipe_id); } sub cmd_addrecipe { # FIXME Make it possible to provide name and uri as command line arguments # instead of solely interactive mode. my ( $recipe_name, $uri, $answer, $sql ); print "Recipe name: "; $recipe_name = ; chomp $recipe_name; print "E.g. http://stupid.domain.name/node/755, urn:isbn:9789127118348#102\n"; print "Recipe uri: "; $uri = ; chomp $uri; $sql = "INSERT INTO recipes (name, uri) VALUES (".$db->quote($recipe_name). ", ".$db->quote($uri).");"; print "$sql\n\n"; print "Add to database? (y/n): "; $answer = ; if ($answer eq "y\n") { $db->do($sql); } $sql = "SELECT id FROM recipes WHERE name=".$db->quote($recipe_name). " AND uri=".$db->quote($uri).";"; my @recipe_id = $db->selectrow_array($sql); return interactive_edit_recipe_ingredients($recipe_id[0]); } sub cmd_showrecipe { # Argument is recipe_id my ( $recipe_id ) = @_; my $recipe_row = $db->selectrow_arrayref("SELECT name, uri, servings FROM recipes WHERE id=".$recipe_id.";"); my $servings; print @$recipe_row[0]; if(defined(@$recipe_row[1])) { printf ", %s\n", @$recipe_row[1]; } else { print "\n"; } if(defined(@$recipe_row[2])) { $servings = @$recipe_row[2]; print "Serves: $servings\n\n" } else { $servings = 1; print "WARNING servings is not set for recipe $recipe_id!\n\n"; } my $contents = $db->selectall_hashref("SELECT * FROM contents WHERE recipe_id=".$recipe_id.";", 'ingredient_id'); for my $content ( keys(%$contents)) { my $ingredientcol = $db->selectcol_arrayref("SELECT name FROM ingredients WHERE id=".$content.";"); printf "%4s %-8s %s\n", $$contents{$content}{quantity}, $$contents{$content}{unit}, @$ingredientcol[0]; } return 1; } sub cmd_searchrecipes { # Argument is search regexp my ( $regex ) = @_; my $i; for my $recipe (@recipes) { $i++; # while (my ($i, $recipe) = each @recipes) { next if not defined($recipe); if(grep(/$regex/i, $recipe->{'name'})) { printf "%4d| %-50s\n", $i-1, $recipe->{'name'}; } } 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 my ( $shopdays ) = @_; my $startdate = DateTime->now(); my $enddate = $startdate->clone(); $shopdays=7 unless $shopdays; $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 $plan_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 $queue_entries = $db->selectall_arrayref("SELECT recipe_id FROM queue;"); my %recipe_count; for my $entry ( @$plan_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 } 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]; } # map {print "| $_ = ${recipe_count{$_}}\n"} sort keys(%recipe_count); my @shop_recipes; for my $recipe ( keys(%recipe_count) ) { my $servings_col = $db->selectcol_arrayref("SELECT servings FROM recipes WHERE id=".$recipe.";"); my $servings; if(defined(@$servings_col[0])) { $servings = @$servings_col[0]; } else { $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++) { push @shop_recipes, $recipe; } } my @shop_ingredients; for my $recipe (@shop_recipes) { # my $contents = $db->selectall_arrayref("SELECT * FROM contents WHERE recipe_id=".$recipe.";"); my $contents = $db->selectall_hashref("SELECT * FROM contents WHERE recipe_id=".$recipe.";", 'ingredient_id'); unless (%$contents) { print "WARNING recipe $recipe contains no ingredients!\n"; } for my $content ( keys(%$contents)) { my %shop; # print %$contents, "\n"; my $ingredientcol = $db->selectcol_arrayref("SELECT name FROM ingredients WHERE id=".$content.";"); # print $content, " ", @$ingredientcol[0], "\n"; # print $$contents{$content}{unit}, $content, " ", @$ingredientcol[0], "\n"; # print keys(%$contents), "\n"; $shop{id} = $content; $shop{quantity} = $$contents{$content}{quantity}; # $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; 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 %-40s", $shop->{quantity}, $shop->{unit}, $shop->{ingredient}; for ( @{$shop->{recipe}} ) { print get_recipe_name($_), ", "; } print "\n"; } return 1; } # MAIN PROGRAM ################################################################ read_recipe_db; if ($ARGV[0]) { if ( $ARGV[0] eq "help") { print "Command failed!\n" unless (cmd_help >= 0); } elsif ( $ARGV[0] eq "addrecipe") { print "Command failed!\n" unless (cmd_addrecipe() >= 0); } elsif ( $ARGV[0] eq "searchrecipes") { print "Command failed!\n" unless (cmd_searchrecipes($ARGV[1]) >= 0); } elsif ( $ARGV[0] eq "editrecipe") { print "Command failed!\n" unless (cmd_editrecipe($ARGV[1]) >= 0); } elsif ( $ARGV[0] eq "showrecipe") { print "Command failed!\n" unless (cmd_showrecipe($ARGV[1]) >= 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); } 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); } 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 "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 { print "Unknown command $ARGV[0]\n"; exit 1; } } else { cmd_showplan; }