PNA.fi koodi

food.pl 14KB

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