PNA.fi koodi

food.pl 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. #!/usr/bin/env perl
  2. # Ruokalistaparseri v1.5.1
  3. # Copyright (c) 2007-2010 Timo Sirainen
  4. # This is Public Domain
  5. use strict;
  6. use POSIX qw(strftime mktime);
  7. use HTML::TokeParser;
  8. use HTML::Entities;
  9. use vars qw(@day_names);
  10. @day_names = ( "Maanantai", "Tiistai", "Keskiviikko", "Torstai",
  11. "Perjantai", "Lauantai", "Sunnuntai" );
  12. require 'amica.pl';
  13. require 'sodexo.pl';
  14. require 'juvenes.pl';
  15. require 'pky.pl';
  16. my @allergies = ( "M", "L", "VL", "G", "K", "Ve" );
  17. my %allergy_descriptions = (
  18. "M" => "Maidoton",
  19. "L" => "Laktoositon",
  20. "VL" => "Vähälaktoosinen",
  21. "G" => "Gluteiiniton",
  22. "K" => "Kasvis",
  23. "Ve" => "Vegaani"
  24. );
  25. my $global_prefix = "";
  26. my $use_old = 0; # 1 is good for testing, 0 for production system!
  27. my @unordered;
  28. my @l = localtime;
  29. my $this_week = strftime("%V", @l);
  30. push @unordered, get_juvenes_restaurants($use_old);
  31. push @unordered, get_amica_restaurant($use_old);
  32. push @unordered, get_sodexo_restaurants($use_old);
  33. push @unordered, get_pky_restaurants($use_old, $l[6] == 0 || $l[6] == 6);
  34. my $max_week = 0;
  35. foreach my $r (@unordered) {
  36. my $week = @{$r}[2];
  37. $max_week = $week if ($week > $max_week || $week == 1);
  38. }
  39. if ($l[6] != 0 && $this_week != $max_week) {
  40. # it's not sunday, don't force next week's menu yet
  41. $max_week = $this_week;
  42. }
  43. my $stamp = time() - 3600*24*7;
  44. my $max_week_daterange = "";
  45. if ($max_week >= 1 && $max_week <= 52) {
  46. # figure out the date range
  47. for (;;) {
  48. my $stamp_week = strftime("%V", localtime($stamp));
  49. last if ($stamp_week == $max_week);
  50. $stamp += 3600*24;
  51. }
  52. my @l1 = localtime($stamp);
  53. my @l2 = localtime($stamp + 3600*24*6);
  54. if ($l1[4] == $l2[4]) {
  55. # same month
  56. $max_week_daterange = $l1[3]."-".$l2[3].".".($l1[4]+1).".";
  57. } else {
  58. # different months
  59. $max_week_daterange = $l1[3].".".($l1[4]+1)."-".$l2[3].".".($l2[4]+1).".";
  60. }
  61. $max_week_daterange = " ($max_week_daterange)"
  62. }
  63. my $file_header = '<?xml version="1.0" encoding="iso-8859-1"?>
  64. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  65. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fi" lang="fi">
  66. <head>
  67. <title>Ruokalistat</title>
  68. <link rel="stylesheet" type="text/css" href="'.$global_prefix.'/ruoka.css" />
  69. </head>
  70. <body onload="set_allergies()">
  71. <form method="get" action="/cgi-bin/food.cgi">
  72. ';
  73. my $file_footer = "<div class=\"footer\">Päivitetty ".
  74. strftime("%d.%m.%Y %H:%M:%S", localtime).
  75. " <input type=\"submit\" value=\"Päivitä nyt\" />".
  76. " / Palaute <a href=\"mailto:lamperi+pna\@gmail.com\">lamperi+pna\@gmail.com</a>".
  77. " / <a href=\"$global_prefix/code.html\">Koodit täältä</a>".
  78. " / <a href=\"$global_prefix/pna.html\">Mikä on PNA?</a>".
  79. "</div>\n</form>\n</body></html>\n";
  80. sub find_last_day_with_foods {
  81. my $restaurants_ref = shift;
  82. my $last_day = 0;
  83. foreach my $r (@${restaurants_ref}) {
  84. my ($title, $open_hours, $week, $week_foods_ref) = @{$r};
  85. my @week_foods = @{$week_foods_ref};
  86. for (my $day = 0; $day < 7; $day++) {
  87. if (defined($week_foods[$day])) {
  88. $last_day = $day if ($day > $last_day);
  89. }
  90. }
  91. }
  92. return $last_day;
  93. }
  94. sub write_days_header {
  95. my ($fout, $day, $last_day) = @_;
  96. print $fout " <span class=\"days\">";
  97. for (my $i = 0; $i <= $last_day; $i++) {
  98. if ($i == $day) {
  99. print $fout $day_names[$i]." ";
  100. } else {
  101. print $fout "<a href=\"".($i+1).".html\">".$day_names[$i]."</a> ";
  102. }
  103. }
  104. if ($day < 0) {
  105. print $fout "Taulukko";
  106. } else {
  107. print $fout "<a href=\"table.html\">Taulukko</a>";
  108. }
  109. print $fout "</span>\n";
  110. }
  111. sub write_prefix_header {
  112. my ($fout, $prefix, $day) = @_;
  113. $day = "table" if ($day == 0);
  114. print $fout "<span class=\"location\">";
  115. if ($prefix eq "") {
  116. print $fout "Kaikki ";
  117. } else {
  118. print $fout "<a href=\"$global_prefix/$day.html\">Kaikki</a> ";
  119. }
  120. if ($prefix eq "tay/") {
  121. print $fout "TaY ";
  122. } else {
  123. print $fout "<a href=\"$global_prefix/tay/$day.html\">TaY</a> ";
  124. }
  125. if ($prefix eq "tays/") {
  126. print $fout "TAYS ";
  127. } else {
  128. print $fout "<a href=\"$global_prefix/tays/$day.html\">TAYS</a> ";
  129. }
  130. if ($prefix eq "tty/") {
  131. print $fout "TTY ";
  132. } else {
  133. print $fout "<a href=\"$global_prefix/tty/$day.html\">TTY</a> ";
  134. }
  135. print $fout "</span>\n";
  136. }
  137. sub write_day {
  138. my ($day, $header, $outfname, $last_day, $restaurants_ref, $prefix) = @_;
  139. my @restaurants = @{$restaurants_ref};
  140. open(my $fout, ">$outfname") || die ("Can't create file $outfname");
  141. print $fout "$file_header<h1>$header</h1>\n";
  142. # print weekday links
  143. print $fout "<div class=\"title\">\n";
  144. write_days_header($fout, $day, $last_day);
  145. print $fout " <span class=\"allergy\">Näytä: ";
  146. foreach my $a (@allergies) {
  147. print $fout "<input type=\"checkbox\" name=\"allergy_$a\" id=\"allergy_$a\" onclick=\"highlight()\" />";
  148. print $fout "<span title=\"".$allergy_descriptions{$a}."\">$a</span>";
  149. }
  150. print $fout "</span>\n";
  151. write_prefix_header($fout, $prefix, $day+1);
  152. print $fout "</div>\n";
  153. # print foods
  154. my $foodnum = 0;
  155. my %eatable_food_numbers;
  156. my %maybe_eatable_food_numbers;
  157. my $class = "left";
  158. print $fout "<div class=\"foods\"><div class=\"$class\">\n";
  159. foreach my $r (@restaurants) {
  160. my ($title, $open_hours, $week, $week_foods_ref, $info_ref) = @{$r};
  161. my ($title2, $url, $lazy_allergies, $info_class) = @{$info_ref};
  162. my @week_foods = @{$week_foods_ref};
  163. if (defined($week_foods[$day]) || $day < 5) {
  164. # Bio+Kliininen often have the same foods
  165. next if (try_merge_bio_kliininen(\$title, $day));
  166. if ($info_class ne $class) {
  167. $class = $info_class;
  168. print $fout "</div><div class=\"$class\">\n";
  169. }
  170. $url =~ s/&/&amp;/g;
  171. print $fout "<h2><a href=\"$url\">$title</a></h2>\n";
  172. if ($title =~ /Sodexo/ || !defined($week_foods[$day])) {
  173. print $fout "<p class=\"missing\">Ruokalistaa ei saatavilla.</p>";
  174. next;
  175. }
  176. if ($week ne "" && $week != $max_week) {
  177. if ($week > $max_week || ($week == 1 && $max_week == 52)) {
  178. # early..
  179. print $fout "<p class=\"nextweek\">Viikon $week ruokalista:</p>";
  180. } else {
  181. print $fout "<p class=\"missing\">Saatavilla vain viikon $week ruokalista.</p>";
  182. next;
  183. }
  184. }
  185. if (scalar(@{$week_foods[$day]}) == 0) {
  186. print $fout "<p class=\"missing\">Ei ruokatietoja päivälle.</p>";
  187. next;
  188. }
  189. print $fout "<ul class=\"food\">\n";
  190. foreach my $food (@{$week_foods[$day]}) {
  191. my $output = "";
  192. my %total_allergies;
  193. my %maybe_allergies;
  194. my $part_count = 0;
  195. foreach my $part (split("\n", $food)) {
  196. next if ($part =~ /^(Peruna|Riisi) /); # who cares?
  197. # fries: well, maybe we do care, but we don't care about allergy stuff
  198. # and keep it in the same line as the previous food so as not to
  199. # waste visible space
  200. my $fries = ($part =~ /^(Tikkuperunat|Ranskalaiset perunat)/);
  201. $part_count++;
  202. # add missing () around allergies
  203. $part =~ s/ (([MLGK]|VL|Ve|Veg|Hot)(,([MLGK]|VL|Ve|Veg|Hot|))+)$/ ($1)/;
  204. if ($part =~ /^(.*) \(([^\)]+)\)$/) {
  205. # fix allergy issues
  206. my ($food, $allergy) = ($1, $2);
  207. # standardization
  208. $allergy =~ s/Kasvis/K/g;
  209. $allergy =~ s/([MLGK]|VL)([MLGK]|VL)/$1,$2/g;
  210. # spaces to commas
  211. $allergy =~ s/saatavana[: ]+(.*)$/eriks: $1/;
  212. $allergy =~ s/ +/,/g;
  213. # remove double commas
  214. $allergy =~ s/,+/,/g;
  215. # eriks: standardization
  216. $allergy =~ s/,?eriks:,?/, eriks: /g;
  217. # remove extra commas/spaces from beginning/end
  218. $allergy =~ s/^[, ]+//;
  219. $allergy =~ s/[, ]+$//;
  220. $part = "$food ($allergy)";
  221. }
  222. $output .= "<br />\n" if ($output ne "" && !$fries);
  223. if ($part =~ /Saatavana myös: (.*)/) {
  224. # standardize allergy stuff
  225. my $alt = $1;
  226. $alt =~ s/^\((.*)\)$/$1/;
  227. $alt =~ s/[, ]+/,/g;
  228. $alt =~ s/^,+//;
  229. $alt =~ s/,+$//;
  230. $part =~ s/\)[- ]*Saatavana myös:.*/, eriks: $alt)/;
  231. $part =~ s/[- ]*Saatavana myös:.*/ (eriks: $alt)/;
  232. }
  233. if ($part =~ /^(.*)(\([^\)]+\))$/) {
  234. my ($text, $allergy) = ($1, $2);
  235. if ($fries) {
  236. $output .= ", $text";
  237. } else {
  238. $output .= "$text <span class=\"allergy\">$allergy</span>";
  239. }
  240. $allergy =~ s/^\((.*)\)$/$1/;
  241. $allergy =~ s/ *eriks: //;
  242. my %this_allergies;
  243. foreach my $a (split(/[, ]/, $allergy)) {
  244. foreach my $al (@allergies) {
  245. if ($a eq $al) {
  246. $this_allergies{$a} = 1;
  247. last;
  248. }
  249. }
  250. }
  251. # is M=L always correct? not at least in all restaurants..
  252. #$this_allergies{"L"} = 1 if ($this_allergies{"M"});
  253. $this_allergies{"VL"} = 1 if ($this_allergies{"L"});
  254. foreach my $a (keys %this_allergies) {
  255. $total_allergies{$a}++;
  256. $maybe_allergies{$a}++;
  257. }
  258. if ($lazy_allergies =~ /M/) {
  259. # L might mean M
  260. if ($this_allergies{"L"} && !$this_allergies{"M"}) {
  261. $maybe_allergies{"M"}++;
  262. }
  263. }
  264. } else {
  265. if ($lazy_allergies eq "all") {
  266. # no allergy info, make everything maybe
  267. foreach my $a (@allergies) {
  268. $maybe_allergies{$a}++;
  269. }
  270. }
  271. $output .= $part;
  272. }
  273. }
  274. my $allergy_output = "";
  275. foreach my $a (@allergies) {
  276. if ($total_allergies{$a} == $part_count) {
  277. if (!defined($eatable_food_numbers{$a})) {
  278. $eatable_food_numbers{$a} = "";
  279. } else {
  280. $eatable_food_numbers{$a} .= ",";
  281. }
  282. $eatable_food_numbers{$a} .= $foodnum;
  283. } elsif ($maybe_allergies{$a} == $part_count) {
  284. if (!defined($maybe_eatable_food_numbers{$a})) {
  285. $maybe_eatable_food_numbers{$a} = "";
  286. } else {
  287. $maybe_eatable_food_numbers{$a} .= ",";
  288. }
  289. $maybe_eatable_food_numbers{$a} .= $foodnum;
  290. }
  291. }
  292. print $fout " <li id=\"f$foodnum\">$output</li>\n";
  293. $foodnum++;
  294. }
  295. print $fout "</ul>\n";
  296. }
  297. }
  298. # write allergy scripts
  299. print $fout '<script type="text/javascript" src="'.$global_prefix.'/ruoka.js"></script>';
  300. print $fout '<script type="text/javascript">';
  301. print $fout "var eatable_foods = [];";
  302. print $fout "var maybe_eatable_foods = [];";
  303. foreach my $a (@allergies) {
  304. print $fout "eatable_foods[\"$a\"] = [".$eatable_food_numbers{$a}."];\n";
  305. print $fout "maybe_eatable_foods[\"$a\"] = [".$maybe_eatable_food_numbers{$a}."];\n";
  306. }
  307. my @allergy_strings = map('"'.$_.'"', @allergies);
  308. print $fout "var allergies = [".join(",", @allergy_strings)."];\n";
  309. print $fout "var food_count = $foodnum\n";
  310. print $fout "</script>\n";
  311. print $fout "</div></div>$file_footer";
  312. close $fout;
  313. }
  314. sub write_all_days {
  315. my ($restaurants_ref, $prefix, $title) = @_;
  316. my $last_day = find_last_day_with_foods($restaurants_ref);
  317. for (my $day = 0; $day < 7; $day++) {
  318. my $outfname = $prefix.($day+1).".html";
  319. if ($day > $last_day) {
  320. unlink($outfname);
  321. next;
  322. }
  323. my $header = $day_names[$day]." - $title vko $max_week$max_week_daterange";
  324. write_day($day, $header, $outfname, $last_day, $restaurants_ref, $prefix);
  325. }
  326. }
  327. sub write_table {
  328. my ($restaurants_ref, $prefix, $title) = @_;
  329. my @restaurants = @{$restaurants_ref};
  330. my $last_day = find_last_day_with_foods($restaurants_ref);
  331. my $outfname = $prefix."table.html";
  332. open(my $fout, ">$outfname") || die ("Can't create file $outfname");
  333. my $header = "$title vko $max_week$max_week_daterange";
  334. print $fout "$file_header<h1>$header</h1>\n";
  335. print $fout "<div class=\"title\">\n";
  336. write_days_header($fout, -1, $last_day);
  337. write_prefix_header($fout, $prefix, 0);
  338. print $fout "</div><table border=\"1\"><tr><th>Päivä</th>";
  339. foreach my $r (@restaurants) {
  340. my ($title, $open_hours, $week, $week_foods_ref, $info_ref) = @{$r};
  341. my ($title2, $url) = @{$info_ref};
  342. $url =~ s/&/&nbsp;/g;
  343. print $fout "<th><a href=\"$url\">$title</a></th>";
  344. }
  345. print $fout "</tr>\n";
  346. for (my $day = 0; $day <= $last_day; $day++) {
  347. print $fout "<tr><td>".$day_names[$day]."</td>\n";
  348. foreach my $r (@restaurants) {
  349. my ($title, $open_hours, $week, $week_foods_ref, $info_ref) = @{$r};
  350. my @week_foods = @{$week_foods_ref};
  351. if (defined($week_foods[$day]) && ($week eq "" || $week == $max_week)) {
  352. print $fout "<td><ul>\n";
  353. foreach my $food (@{$week_foods[$day]}) {
  354. print $fout "<li>$food</li>";
  355. }
  356. print $fout "</ul></td>\n";
  357. } else {
  358. print $fout "<td></td>\n";
  359. }
  360. }
  361. print $fout "</tr>\n";
  362. }
  363. print $fout "</table>$file_footer";
  364. close $fout;
  365. }
  366. sub get_restaurants_sorted {
  367. my @restaurants = @_;
  368. my @out;
  369. foreach my $r (@restaurants) {
  370. push @out, $r if (@{@{$r}[4]}[3] eq "left");
  371. }
  372. foreach my $r (@restaurants) {
  373. push @out, $r if (@{@{$r}[4]}[3] eq "right");
  374. }
  375. foreach my $r (@restaurants) {
  376. my @e = @{@{$r}[4]};
  377. push @out, $r if ($e[3] eq "middle" && $e[1] !~ /TAMK/);
  378. }
  379. foreach my $r (@restaurants) {
  380. my @e = @{@{$r}[4]};
  381. push @out, $r if ($e[3] eq "middle" && $e[1] =~ /TAMK/);
  382. }
  383. return @out;
  384. }
  385. sub get_restaurants_with_prefix {
  386. my $prefix = shift;
  387. my @out;
  388. foreach my $r (@_) {
  389. my $name = @{$r}[0];
  390. if ($name =~ /^\($prefix\)/) {
  391. push @out, $r;
  392. }
  393. }
  394. return get_restaurants_sorted(@out);
  395. }
  396. my $tty_title = "TTY:n ruokalistat";
  397. my @tty = get_restaurants_with_prefix("TTY", @unordered);
  398. write_all_days(\@tty, "tty/", $tty_title);
  399. write_table(\@tty, "tty/", $tty_title);
  400. my $tay_title = "Tampereen yliopiston ruokalistat";
  401. my @tay = get_restaurants_with_prefix("TaY", @unordered);
  402. write_all_days(\@tay, "tay/", $tay_title);
  403. write_table(\@tay, "tay/", $tay_title);
  404. my $tays_title = "TAYS:n ruokalistat";
  405. my @tays = get_restaurants_with_prefix("TAYS", @unordered);
  406. write_all_days(\@tays, "tays/", $tays_title);
  407. write_table(\@tays, "tays/", $tays_title);
  408. foreach my $r (@unordered) {
  409. if (@{$r}[0] =~ /^\(TaY\)/) {
  410. @{@{$r}[4]}[3] = "left";
  411. }
  412. if (@{$r}[0] =~ /^\(TTY\)/) {
  413. @{@{$r}[4]}[3] = "right";
  414. }
  415. if (@{$r}[0] =~ /^\(TAYS\)/) {
  416. @{@{$r}[4]}[3] = "middle";
  417. }
  418. }
  419. my $all_title = "Tampereen yliopistojen ruokalistat";
  420. my @all_restaurants = get_restaurants_sorted(@unordered);
  421. # move fusion kitchen last
  422. my @fusion = splice(@all_restaurants, 1, 1);
  423. splice(@all_restaurants, 4, 0, @fusion);
  424. write_all_days(\@all_restaurants, "", $all_title);
  425. write_table(\@all_restaurants, "", $all_title);