PNA.fi koodi

food.pl 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. #!/usr/bin/env perl
  2. # Ruokalistaparseri v1.5.4
  3. # Copyright (c) 2007-2010 Timo Sirainen
  4. # 2011-2013 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>
  72. <div id="notice" style="border: 1px solid black; border-radius: 5px; padding: 5px;">
  73. PNA.fi on kolmannen osapuolen tarjoama palvelu. En voi taata ruokalistojen oikeellisuutta.
  74. Jos huomaat ruokalistassa virheen, nopeiten asia korjaantuu kun lähetät minulle siitä sähköpostia: <a href="mailto:lamperi+pna@gmail.com">lamperi+pna@gmail.com</a>
  75. </div>
  76. <form method="get" action="/cgi-bin/food.cgi">
  77. ';
  78. my $file_footer = "<div class=\"footer\">Päivitetty ".
  79. strftime("%d.%m.%Y %H:%M:%S", localtime).
  80. " <input type=\"submit\" value=\"Päivitä nyt\" />".
  81. " / Palaute <a href=\"mailto:lamperi+pna\@gmail.com\">lamperi+pna\@gmail.com</a>".
  82. " / <a href=\"$global_prefix/code.html\">Koodit täältä</a>".
  83. " / <a href=\"$global_prefix/pna.html\">Mikä on PNA?</a>".
  84. "</div>\n</form>\n</body></html>\n";
  85. sub find_last_day_with_foods {
  86. my $restaurants_ref = shift;
  87. my $last_day = 0;
  88. foreach my $r (@${restaurants_ref}) {
  89. my ($title, $open_hours, $week, $week_foods_ref) = @{$r};
  90. my @week_foods = @{$week_foods_ref};
  91. for (my $day = 0; $day < 7; $day++) {
  92. if (defined($week_foods[$day])) {
  93. $last_day = $day if ($day > $last_day);
  94. }
  95. }
  96. }
  97. return $last_day;
  98. }
  99. sub write_days_header {
  100. my ($fout, $day, $last_day) = @_;
  101. print $fout " <span class=\"days\">";
  102. for (my $i = 0; $i <= $last_day; $i++) {
  103. if ($i == $day) {
  104. print $fout $day_names[$i]." ";
  105. } else {
  106. print $fout "<a href=\"".($i+1).".html\">".$day_names[$i]."</a> ";
  107. }
  108. }
  109. if ($day < 0) {
  110. print $fout "Taulukko";
  111. } else {
  112. print $fout "<a href=\"table.html\">Taulukko</a>";
  113. }
  114. print $fout "</span>\n";
  115. }
  116. sub write_prefix_header {
  117. my ($fout, $prefix, $day) = @_;
  118. $day = "table" if ($day == 0);
  119. print $fout "<span class=\"location\">";
  120. if ($prefix eq "") {
  121. print $fout "Kaikki ";
  122. } else {
  123. print $fout "<a href=\"$global_prefix/$day.html\">Kaikki</a> ";
  124. }
  125. if ($prefix eq "tay/") {
  126. print $fout "TaY ";
  127. } else {
  128. print $fout "<a href=\"$global_prefix/tay/$day.html\">TaY</a> ";
  129. }
  130. if ($prefix eq "tays/") {
  131. print $fout "TAYS ";
  132. } else {
  133. print $fout "<a href=\"$global_prefix/tays/$day.html\">TAYS</a> ";
  134. }
  135. if ($prefix eq "tty/") {
  136. print $fout "TTY ";
  137. } else {
  138. print $fout "<a href=\"$global_prefix/tty/$day.html\">TTY</a> ";
  139. }
  140. print $fout "</span>\n";
  141. }
  142. sub write_day {
  143. my ($day, $header, $outfname, $last_day, $restaurants_ref, $prefix) = @_;
  144. my @restaurants = @{$restaurants_ref};
  145. open(my $fout, ">$outfname") || die ("Can't create file $outfname");
  146. print $fout "$file_header<h1>$header</h1>\n";
  147. # print weekday links
  148. print $fout "<div class=\"title\">\n";
  149. write_days_header($fout, $day, $last_day);
  150. print $fout " <span class=\"allergy\">Näytä: ";
  151. foreach my $a (@allergies) {
  152. print $fout "<input type=\"checkbox\" name=\"allergy_$a\" id=\"allergy_$a\" onclick=\"highlight()\" />";
  153. print $fout "<span title=\"".$allergy_descriptions{$a}."\">$a</span>";
  154. }
  155. print $fout "</span>\n";
  156. write_prefix_header($fout, $prefix, $day+1);
  157. print $fout "</div>\n";
  158. # print foods
  159. my $foodnum = 0;
  160. my %eatable_food_numbers;
  161. my %maybe_eatable_food_numbers;
  162. my $class = "left";
  163. print $fout "<div class=\"foods\"><div class=\"$class\">\n";
  164. foreach my $r (@restaurants) {
  165. my ($title, $open_hours, $week, $week_foods_ref, $info_ref) = @{$r};
  166. my ($title2, $url, $lazy_allergies, $info_class) = @{$info_ref};
  167. my @week_foods = @{$week_foods_ref};
  168. if (defined($week_foods[$day]) || $day < 5) {
  169. # Bio+Kliininen often have the same foods
  170. next if (try_merge_bio_kliininen(\$title, $day));
  171. if ($info_class ne $class) {
  172. $class = $info_class;
  173. print $fout "</div><div class=\"$class\">\n";
  174. }
  175. $url =~ s/&/&amp;/g;
  176. print $fout "<h2><a href=\"$url\">$title</a></h2>\n";
  177. if (!defined($week_foods[$day])) {
  178. print $fout "<p class=\"missing\">Ruokalistaa ei saatavilla.</p>";
  179. next;
  180. }
  181. if ($week ne "" && $week != $max_week) {
  182. if ($week > $max_week || ($week == 1 && $max_week == 52)) {
  183. # early..
  184. print $fout "<p class=\"nextweek\">Viikon $week ruokalista:</p>";
  185. } else {
  186. print $fout "<p class=\"missing\">Saatavilla vain viikon $week ruokalista.</p>";
  187. next;
  188. }
  189. }
  190. if (scalar(@{$week_foods[$day]}) == 0) {
  191. print $fout "<p class=\"missing\">Ei ruokatietoja päivälle.</p>";
  192. next;
  193. }
  194. print $fout "<ul class=\"food\">\n";
  195. foreach my $food (@{$week_foods[$day]}) {
  196. my $output = "";
  197. my %total_allergies;
  198. my %maybe_allergies;
  199. my $part_count = 0;
  200. foreach my $part (split("\n", $food)) {
  201. next if ($part =~ /^(Peruna|Riisi) /); # who cares?
  202. # fries: well, maybe we do care, but we don't care about allergy stuff
  203. # and keep it in the same line as the previous food so as not to
  204. # waste visible space
  205. my $fries = ($part =~ /^(Tikkuperunat|Ranskalaiset perunat)/);
  206. $part_count++;
  207. # add missing () around allergies
  208. $part =~ s/ (([MLGK]|VL|Ve|Veg|Hot)(,([MLGK]|VL|Ve|Veg|Hot|))+)$/ ($1)/;
  209. if ($part =~ /^(.*) \(([^\)]+)\)$/) {
  210. # fix allergy issues
  211. my ($food, $allergy) = ($1, $2);
  212. # standardization
  213. $allergy =~ s/Kasvis/K/g;
  214. $allergy =~ s/([MLGK]|VL)([MLGK]|VL)/$1,$2/g;
  215. # spaces to commas
  216. $allergy =~ s/saatavana[: ]+(.*)$/eriks: $1/;
  217. $allergy =~ s/ +/,/g;
  218. # remove double commas
  219. $allergy =~ s/,+/,/g;
  220. # eriks: standardization
  221. $allergy =~ s/,?eriks:,?/, eriks: /g;
  222. # remove extra commas/spaces from beginning/end
  223. $allergy =~ s/^[, ]+//;
  224. $allergy =~ s/[, ]+$//;
  225. $part = "$food ($allergy)";
  226. }
  227. $output .= "<br />\n" if ($output ne "" && !$fries);
  228. if ($part =~ /Saatavana myös: (.*)/) {
  229. # standardize allergy stuff
  230. my $alt = $1;
  231. $alt =~ s/^\((.*)\)$/$1/;
  232. $alt =~ s/[, ]+/,/g;
  233. $alt =~ s/^,+//;
  234. $alt =~ s/,+$//;
  235. $part =~ s/\)[- ]*Saatavana myös:.*/, eriks: $alt)/;
  236. $part =~ s/[- ]*Saatavana myös:.*/ (eriks: $alt)/;
  237. }
  238. if ($part =~ /^(.*)(\([^\)]+\))$/) {
  239. my ($text, $allergy) = ($1, $2);
  240. if ($fries) {
  241. $output .= ", $text";
  242. } else {
  243. $output .= "$text <span class=\"allergy\">$allergy</span>";
  244. }
  245. $allergy =~ s/^\((.*)\)$/$1/;
  246. $allergy =~ s/ *eriks: //;
  247. my %this_allergies;
  248. foreach my $a (split(/[, ]/, $allergy)) {
  249. foreach my $al (@allergies) {
  250. if ($a eq $al) {
  251. $this_allergies{$a} = 1;
  252. last;
  253. }
  254. }
  255. }
  256. # is M=L always correct? not at least in all restaurants..
  257. #$this_allergies{"L"} = 1 if ($this_allergies{"M"});
  258. $this_allergies{"VL"} = 1 if ($this_allergies{"L"});
  259. foreach my $a (keys %this_allergies) {
  260. $total_allergies{$a}++;
  261. $maybe_allergies{$a}++;
  262. }
  263. if ($lazy_allergies =~ /M/) {
  264. # L might mean M
  265. if ($this_allergies{"L"} && !$this_allergies{"M"}) {
  266. $maybe_allergies{"M"}++;
  267. }
  268. }
  269. } else {
  270. if ($lazy_allergies eq "all") {
  271. # no allergy info, make everything maybe
  272. foreach my $a (@allergies) {
  273. $maybe_allergies{$a}++;
  274. }
  275. }
  276. $output .= $part;
  277. }
  278. }
  279. my $allergy_output = "";
  280. foreach my $a (@allergies) {
  281. if ($total_allergies{$a} == $part_count) {
  282. if (!defined($eatable_food_numbers{$a})) {
  283. $eatable_food_numbers{$a} = "";
  284. } else {
  285. $eatable_food_numbers{$a} .= ",";
  286. }
  287. $eatable_food_numbers{$a} .= $foodnum;
  288. } elsif ($maybe_allergies{$a} == $part_count) {
  289. if (!defined($maybe_eatable_food_numbers{$a})) {
  290. $maybe_eatable_food_numbers{$a} = "";
  291. } else {
  292. $maybe_eatable_food_numbers{$a} .= ",";
  293. }
  294. $maybe_eatable_food_numbers{$a} .= $foodnum;
  295. }
  296. }
  297. print $fout " <li id=\"f$foodnum\">$output</li>\n";
  298. $foodnum++;
  299. }
  300. print $fout "</ul>\n";
  301. }
  302. }
  303. # write allergy scripts
  304. print $fout '<script type="text/javascript" src="'.$global_prefix.'/ruoka.js"></script>';
  305. print $fout '<script type="text/javascript">';
  306. print $fout "var eatable_foods = [];";
  307. print $fout "var maybe_eatable_foods = [];";
  308. foreach my $a (@allergies) {
  309. print $fout "eatable_foods[\"$a\"] = [".$eatable_food_numbers{$a}."];\n";
  310. print $fout "maybe_eatable_foods[\"$a\"] = [".$maybe_eatable_food_numbers{$a}."];\n";
  311. }
  312. my @allergy_strings = map('"'.$_.'"', @allergies);
  313. print $fout "var allergies = [".join(",", @allergy_strings)."];\n";
  314. print $fout "var food_count = $foodnum\n";
  315. print $fout "window.onload = function() { set_allergies(); show_warning(); };\n";
  316. print $fout "</script>\n";
  317. print $fout "</div></div>$file_footer";
  318. close $fout;
  319. }
  320. sub write_all_days {
  321. my ($restaurants_ref, $prefix, $title) = @_;
  322. my $last_day = find_last_day_with_foods($restaurants_ref);
  323. for (my $day = 0; $day < 7; $day++) {
  324. my $outfname = $prefix.($day+1).".html";
  325. if ($day > $last_day) {
  326. unlink($outfname);
  327. next;
  328. }
  329. my $header = $day_names[$day]." - $title vko $max_week$max_week_daterange";
  330. write_day($day, $header, $outfname, $last_day, $restaurants_ref, $prefix);
  331. }
  332. }
  333. sub write_table {
  334. my ($restaurants_ref, $prefix, $title) = @_;
  335. my @restaurants = @{$restaurants_ref};
  336. my $last_day = find_last_day_with_foods($restaurants_ref);
  337. my $outfname = $prefix."table.html";
  338. open(my $fout, ">$outfname") || die ("Can't create file $outfname");
  339. my $header = "$title vko $max_week$max_week_daterange";
  340. print $fout "$file_header<h1>$header</h1>\n";
  341. print $fout "<div class=\"title\">\n";
  342. write_days_header($fout, -1, $last_day);
  343. write_prefix_header($fout, $prefix, 0);
  344. print $fout "</div><table border=\"1\"><tr><th>Päivä</th>";
  345. foreach my $r (@restaurants) {
  346. my ($title, $open_hours, $week, $week_foods_ref, $info_ref) = @{$r};
  347. my ($title2, $url) = @{$info_ref};
  348. $url =~ s/&/&nbsp;/g;
  349. print $fout "<th><a href=\"$url\">$title</a></th>";
  350. }
  351. print $fout "</tr>\n";
  352. for (my $day = 0; $day <= $last_day; $day++) {
  353. print $fout "<tr><td>".$day_names[$day]."</td>\n";
  354. foreach my $r (@restaurants) {
  355. my ($title, $open_hours, $week, $week_foods_ref, $info_ref) = @{$r};
  356. my @week_foods = @{$week_foods_ref};
  357. if (defined($week_foods[$day]) && ($week eq "" || $week == $max_week)) {
  358. print $fout "<td><ul>\n";
  359. foreach my $food (@{$week_foods[$day]}) {
  360. print $fout "<li>$food</li>";
  361. }
  362. print $fout "</ul></td>\n";
  363. } else {
  364. print $fout "<td></td>\n";
  365. }
  366. }
  367. print $fout "</tr>\n";
  368. }
  369. print $fout "</table>$file_footer";
  370. close $fout;
  371. }
  372. sub get_restaurants_sorted {
  373. my @restaurants = @_;
  374. my @out;
  375. foreach my $r (@restaurants) {
  376. push @out, $r if (@{@{$r}[4]}[3] eq "left");
  377. }
  378. foreach my $r (@restaurants) {
  379. push @out, $r if (@{@{$r}[4]}[3] eq "right");
  380. }
  381. foreach my $r (@restaurants) {
  382. my @e = @{@{$r}[4]};
  383. push @out, $r if ($e[3] eq "middle" && $e[1] !~ /TAMK/);
  384. }
  385. foreach my $r (@restaurants) {
  386. my @e = @{@{$r}[4]};
  387. push @out, $r if ($e[3] eq "middle" && $e[1] =~ /TAMK/);
  388. }
  389. return @out;
  390. }
  391. sub get_restaurants_with_prefix {
  392. my $prefix = shift;
  393. my @out;
  394. foreach my $r (@_) {
  395. my $name = @{$r}[0];
  396. if ($name =~ /^\($prefix\)/) {
  397. push @out, $r;
  398. }
  399. }
  400. return get_restaurants_sorted(@out);
  401. }
  402. my $tty_title = "TTY:n ruokalistat";
  403. my @tty = get_restaurants_with_prefix("TTY", @unordered);
  404. write_all_days(\@tty, "tty/", $tty_title);
  405. write_table(\@tty, "tty/", $tty_title);
  406. my $tay_title = "Tampereen yliopiston ruokalistat";
  407. my @tay = get_restaurants_with_prefix("TaY", @unordered);
  408. write_all_days(\@tay, "tay/", $tay_title);
  409. write_table(\@tay, "tay/", $tay_title);
  410. my $tays_title = "TAYS:n ruokalistat";
  411. my @tays = get_restaurants_with_prefix("TAYS", @unordered);
  412. write_all_days(\@tays, "tays/", $tays_title);
  413. write_table(\@tays, "tays/", $tays_title);
  414. foreach my $r (@unordered) {
  415. if (@{$r}[0] =~ /^\(TaY\)/) {
  416. @{@{$r}[4]}[3] = "left";
  417. }
  418. if (@{$r}[0] =~ /^\(TTY\)/) {
  419. @{@{$r}[4]}[3] = "right";
  420. }
  421. if (@{$r}[0] =~ /^\(TAYS\)/) {
  422. @{@{$r}[4]}[3] = "middle";
  423. }
  424. }
  425. my $all_title = "Tampereen yliopistojen ruokalistat";
  426. my @all_restaurants = get_restaurants_sorted(@unordered);
  427. # move fusion kitchen last
  428. my @fusion = splice(@all_restaurants, 1, 1);
  429. splice(@all_restaurants, 4, 0, @fusion);
  430. write_all_days(\@all_restaurants, "", $all_title);
  431. write_table(\@all_restaurants, "", $all_title);