123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- #!/usr/bin/env python3
-
- # Ruokalistaparseri
- # Copyright (c) 2016 Toni Fadjukoff
-
- # Based on food.pl by
- # Copyright (c) 2007-2010 Timo Sirainen
- # 2011-2016 Toni Fadjukoff
- # This is Public Domain
-
- import sys
-
- day_names = [ "Maanantai", "Tiistai", "Keskiviikko", "Torstai",
- "Perjantai", "Lauantai", "Sunnuntai" ]
-
- import amica
- import sodexo
- import juvenes
- import campusravita
-
- allergies = [ "M", "L", "VL", "G", "K", "Ve" ]
- allergy_descriptions = {
- "M": "Maidoton",
- "L": "Laktoositon","VL": "Vähälaktoosinen",
- "G": "Gluteiiniton",
- "K": "Kasvis",
- "Ve": "Vegaani"
- }
-
- import os
- import time
- import datetime
- import re
-
- global_prefix = "";
- use_old = True; # 1 is good for testing, 0 for production system!
- unordered = []
-
- l = time.localtime()
- this_week = datetime.datetime.now().isocalendar()[1]
-
- unordered += amica.get_restaurants(use_old, this_week)
- unordered += juvenes.get_restaurants(use_old, this_week)
- unordered += sodexo.get_restaurants(use_old, this_week)
- unordered += campusravita.get_restaurants(use_old, this_week)
-
-
- max_week = 0;
- for r in unordered:
- week = r[2]
- max_week = week if week > max_week or week == 1 else max_week
-
- if l[6] != 0 and this_week != max_week:
- # it's not sunday, don't force next week's menu yet
- max_week = this_week
-
- stamp = time.time() - 3600*24*7
- max_week_daterange = ""
- if max_week >= 1 and max_week <= 52:
- # figure out the date range
- while True:
- stamp_week = int(time.strftime("%W", time.localtime(stamp)))
- if stamp_week == max_week:
- break
- stamp += 3600*24
- l1 = time.localtime(stamp)
- l2 = time.localtime(stamp + 3600*24*6)
- if l1[1] == l2[1]:
- # same month
- max_week_daterange = "{monday}-{sunday}.{month}.".format(monday=l1[2], sunday=l2[2], month=l1[1])
- else:
- # different months
- max_week_daterange = "{monday}.{month}.-{sunday}.{next_month}.".format(monday=l1[2], month=l1[1],
- sunday=l2[2], next_month=l2[1])
- max_week_daterange = " (" + str(max_week_daterange) + ")"
-
- file_header = '''<?xml version="1.0" encoding="utf-8"?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fi" lang="fi">
- <head>
- <title>Ruokalistat</title>
- <link rel="stylesheet" type="text/css" href="{resources_prefix}ruoka.css" />
- </head>
- <body>
-
- <div id="notice" style="border: 1px solid black; border-radius: 5px; padding: 5px;">
- PNA.fi on kolmannen osapuolen tarjoama palvelu. En voi taata ruokalistojen oikeellisuutta. Virallisen ruokalistan saat näkyviin siirtymällä ravintolan omille sivuille painamalla sen nimestä. Jos huomaat ruokalistassa virheen, nopeiten virhe saadaan pois näkyvistä kun lähetät minulle siitä sähköpostia: <a href="mailto:lamperi+pna@gmail.com">lamperi+pna@gmail.com</a>
- </div>
-
- <form method="get" action="/cgi-bin/food.cgi">
- '''
-
- file_footer = """
- <div class="footer">Päivitetty {stamp}
- <input type="submit\" value=\"Päivitä nyt\" />
- / Palaute <a href=\"mailto:lamperi+pna@gmail.com\">lamperi+pna@gmail.com</a>
- / <a href="{{resources_prefix}}code.html\">Koodit täältä</a>
- / <a href="{{resources_prefix}}pna.html\">Mikä on PNA?</a>
- </div>
- </form>
- </body>
- </html>
- """.format(stamp=time.strftime("%d.%m.%Y %H:%M:%S"))
-
- def find_last_day_with_foods(restaurants):
- last_day = 0
- for r in restaurants:
- for day in range(7):
- if day in r[3]:
- last_day = day if day > last_day else last_day
- return last_day
-
- def write_days_header(fout, day, last_day):
- fout.write( " <span class=\"days\">")
- for i in range(last_day):
- if i == day:
- fout.write("{0} ".format(day_names[i]))
- else:
- fout.write("<a href=\"{0}.html\">{1}</a> ".format(i+1, day_names[i]))
-
- if day < 0:
- fout.write("Taulukko")
- else:
- fout.write("<a href=\"table.html\">Taulukko</a>")
- fout.write("</span>\n")
-
- def write_prefix_header(fout, prefix, day, resources_prefix):
- day = "table" if day == 0 else day
- fout.write("<span class=\"location\">")
- if prefix == "":
- fout.write("Kaikki ")
- else:
- fout.write("<a href=\"{resources_prefix}{day}.html\">Kaikki</a> ".format(resources_prefix=resources_prefix, day=day))
- if prefix == "tay/":
- fout.write("TaY ")
- else:
- fout.write("<a href=\"{resources_prefix}tay/{day}.html\">TaY</a> ".format(resources_prefix=resources_prefix, day=day))
- if prefix == "tays/":
- fout.write("TAYS ")
- else:
- fout.write("<a href=\"{resources_prefix}tays/{day}.html\">TAYS</a> ".format(resources_prefix=resources_prefix, day=day))
- if prefix == "tty/":
- fout.write("TTY ")
- else:
- fout.write("<a href=\"{resources_prefix}tty/{day}.html\">TTY</a> ".format(resources_prefix=resources_prefix, day=day))
- fout.write("</span>\n")
-
- def write_day(day, header, outfname, last_day, restaurants, prefix, resources_prefix):
- with open(outfname, "w", encoding="utf-8") as fout:
-
- fout.write(file_header.format(resources_prefix=resources_prefix))
- fout.write("<h1>{header}</h1>\n".format(header=header))
- # print weekday links
- fout.write("<div class=\"title\">\n")
- write_days_header(fout, day, last_day)
- fout.write(" <span class=\"allergy\">Näytä: ")
- for a in allergies:
- fout.write("<input type=\"checkbox\" name=\"allergy_{a}\" id=\"allergy_{a}\" onclick=\"highlight()\" />".format(a=a))
- fout.write("<span title=\"{allergy_description}\">{a}</span>".format(allergy_description=allergy_descriptions[a], a=a))
- fout.write("</span>\n")
- write_prefix_header(fout, prefix, day+1, resources_prefix);
- fout.write("</div>\n")
-
- # print foods
- foodnum = 0
- eatable_food_numbers = {}
- maybe_eatable_food_numbers = {}
- for a in allergies:
- eatable_food_numbers[a] = []
- maybe_eatable_food_numbers[a] = []
- css_class = "left"
- fout.write("<div class=\"foods\"><div class=\"{css_class}\">\n".format(css_class=css_class))
- for r in restaurants:
- title, open_hours, week, week_foods, info = r
- title2, url, lazy_allergies, info_class = info[0:4]
- if day in week_foods or day < 5:
- if info_class != css_class:
- css_class = info_class
- fout.write("</div><div class=\"{css_class}\">\n".format(css_class=css_class))
- url = url.replace("&", "&")
- fout.write("<h2><a href=\"{url}\">{title}</a></h2>\n".format(url=url, title=title))
- if not day in week_foods:
- fout.write("<p class=\"missing\">Ruokalistaa ei saatavilla.</p>")
- continue
- if week != "" and week != max_week:
- if week > max_week or (week == 1 and max_week == 52):
- # early..
- fout.write("<p class=\"nextweek\">Viikon {week} ruokalista:</p>".format(week=week))
- else:
- fout.write("<p class=\"missing\">Saatavilla vain viikon {week} ruokalista.</p>".format(week=week))
- continue
- if len(week_foods[day]) == 0:
- fout.write("<p class=\"missing\">Ei ruokatietoja päivälle.</p>")
- continue
- fout.write("<ul class=\"food\">\n")
- for food in week_foods[day]:
- output = []
- total_allergies = {}
- maybe_allergies = {}
- for a in allergies:
- total_allergies[a] = 0
- maybe_allergies[a] = 0
- part_count = 0
- for part in food.split("\n"):
- # who cares?
- if re.match("Peruna|Riisi", part):
- continue
- # fries: well, maybe we do care, but we don't care about allergy stuff
- # and keep it in the same line as the previous food so as not to
- # waste visible space
- (part, fries) = re.subn("Tikkuperunat|Ranskalaiset perunat", "", part)
- fries = fries > 0
- part_count += 1
-
- # add missing () around allergies
- part = re.sub(" (([MLGKA]|VL|Ve|VE|Veg|Hot)(, *([MLGKA]|VL|Ve|VE|Veg|Hot|))+)$", " (\\1)", part)
- match = re.match("^(.*) \\(([^\\)]+)\\)$", part)
- if match:
- # fix allergy issues
- food = match.group(1)
- allergy = match.group(2)
- # standardization
- allergy = re.sub("Kasvis", "K", allergy)
- allergy = re.sub("([MLGK]|VL)([MLGK]|[VL])", "\\1,\\2", allergy)
- # spaces to commas
- allergy = re.sub("saatavana[: ]+(.*)$", "eriks: \\1", allergy)
- allergy = re.sub(" +", ",", allergy)
- # remove double commas
- allergy = re.sub(",+", ",", allergy)
- # eriks: standardization
- allergy = re.sub(",?eriks:,?", ", eriks: ", allergy)
- # remove extra commas/spaces from beginning/end
- allergy = re.sub("^[, ]+", "", allergy)
- allergy = re.sub("[, ]+$", "", allergy)
- part = "{food} ({allergy})".format(food=food, allergy=allergy)
-
- if output and not fries:
- output.append("<br />\n")
-
- match = re.search("Saatavana myös: (.*)", part)
- if match:
- alt = match.group(1)
- alt = re.sub(r"^\((.*)\)$", r"\1", alt)
- alt = re.sub("[, ]+", r",", alt)
- alt = re.sub("^,+", "", alt)
- alt = re.sub(",+$", "", alt)
- part = re.sub(r"\)[- ]*Saatavana myös:.*", "eriks: {alt})".format(alt=alt), part)
- part = re.sub(r"[- ]*Saatavana myös:.*", "(eriks: {alt})".format(alt=alt), part)
-
- match = re.match(r"^(.*)(\([^\)]+\))$", part)
- if match:
- text = match.group(1)
- allergy = match.group(2)
- if fries:
- output.append(", {text}".format(text=text))
- else:
- output.append("{text} <span class=\"allergy\">{allergy}</span>".format(text=text, allergy=allergy))
- allergy = re.sub(r"^\((.*)\)$", r"\1", allergy)
- allergy = re.sub(" *eriks: ", "", allergy)
- this_allergies = set()
- for a in re.split("[, ]", allergy):
- for al in allergies:
- if a == al:
- this_allergies.add(a)
- break
- if "L" in this_allergies:
- this_allergies.add("VL")
- for a in this_allergies:
- if a in total_allergies:
- total_allergies[a] += 1
- if a in maybe_allergies:
- maybe_allergies[a] += 1
- match = re.search("M", lazy_allergies)
- if match:
- if "L" in this_allergies and not "M" in this_allergies:
- maybe_allergies["M"] += 1
- else:
- if lazy_allergies == "all":
- for a in allergies:
- maybe_allergies[a] += 1
-
- output.append(part)
- allergy_output = ""
- for a in allergies:
- if total_allergies[a] == part_count:
- eatable_food_numbers[a].append(foodnum)
- elif maybe_allergies[a]== part_count:
- maybe_eatable_food_numbers[a].append(foodnum)
- fout.write(" <li id=\"f{foodnum}\">{output}</li>\n".format(foodnum=foodnum, output="".join(output)))
- foodnum += 1
- fout.write("</ul>\n")
- # write allergy scripts
- fout.write('<script type="text/javascript" src="{resources_prefix}ruoka.js"></script>'.format(resources_prefix=resources_prefix))
- fout.write('<script type="text/javascript">\n')
- fout.write("var eatable_foods = [];\n")
- fout.write("var maybe_eatable_foods = [];\n")
- for a in allergies:
- fout.write("eatable_foods[\"{a}\"] = [{eatable_food_number}];\n".format(a=a, eatable_food_number=",".join(str(e) for e in eatable_food_numbers[a])))
- fout.write("maybe_eatable_foods[\"{a}\"] = [{maybe_eatable_food_number}];\n".format(a=a, maybe_eatable_food_number=",".join(str(e) for e in maybe_eatable_food_numbers[a])))
- allergy_string = ",".join('"{a}"'.format(a=a) for a in allergies)
- fout.write("var allergies = [{allergies}];\n".format(allergies = allergy_string))
- fout.write("var food_count = {foodnum};\n".format(foodnum = foodnum))
- fout.write("window.onload = function() { set_allergies(); show_warning(); };\n")
- fout.write("</script>\n")
- fout.write("</div></div>{file_footer}".format(file_footer=file_footer.format(resources_prefix=resources_prefix)))
-
- def write_all_days(restaurants, prefix, title, resources_prefix):
- try:
- os.mkdir(prefix)
- except OSError as err:
- pass # hope it already exists
- last_day = find_last_day_with_foods(restaurants);
- for day in range(7):
- outfname = "{prefix}{day}.html".format(prefix=prefix, day=day+1)
- if day > last_day:
- try:
- os.unlink(outfname)
- except OSError as err:
- pass # probably did not exist
- continue
- header = "{day_name} - {title} vko {max_week}{max_week_daterange}".format(day_name=day_names[day], title=title,
- max_week=max_week, max_week_daterange=max_week_daterange)
- write_day(day, header, outfname, last_day, restaurants, prefix, resources_prefix)
-
- def write_table(restaurants, prefix, title, resources_prefix):
- last_day = find_last_day_with_foods(restaurants);
- outfname = "{prefix}table.html".format(prefix=prefix)
- with open(outfname, "w", encoding="utf-8") as fout:
- header = "{title} vko {max_week}{max_week_daterange}".format(title=title, max_week=max_week, max_week_daterange=max_week_daterange)
- fout.write(file_header.format(resources_prefix=resources_prefix))
- fout.write("<h1>{header}</h1>\n".format(header=header))
- fout.write("<div class=\"title\">\n")
-
- write_days_header(fout, -1, last_day)
- write_prefix_header(fout, prefix, 0, resources_prefix)
- fout.write("</div><table border=\"1\"><tr><th>Päivä</th>")
- for r in restaurants:
- (title, open_hours, week, week_foods, info) = r
- (title2, url) = info[0:2]
- url = re.sub("&", " ", url)
- fout.write("<th><a href=\"{url}\">{title}</a></th>".format(url=url, title=title))
- fout.write("</tr>\n")
- for day in range(last_day):
- fout.write("<tr><td>{day_name}</td>\n".format(day_name=day_names[day]))
- for r in restaurants:
- (title, open_hours, week, week_foods, info) = r
- if day in week_foods and (week == "" or week == max_week):
- fout.write("<td><ul>\n")
- for food in week_foods[day]:
- fout.write("<li>{food}</li>".format(food=food))
- fout.write("</ul></td>\n")
- else:
- fout.write("<td></td>\n")
- fout.write("</tr\n")
- fout.write("</table>{file_footer}".format(file_footer=file_footer.format(resources_prefix=resources_prefix)))
-
- def get_restaurants_sorted(restaurants):
- # consider writing comparator
- out = []
- for r in restaurants:
- if r[4][3] == "left":
- out.append(r)
- for r in restaurants:
- if r[4][3] == "right":
- out.append(r)
- for r in restaurants:
- if r[4][3] == "middle" and not re.search("TAMK", r[4][0]):
- out.append(r)
- for r in restaurants:
- if r[4][3] == "middle" and re.search("TAMK", r[4][0]):
- out.append(r)
- return out
-
- def get_restaurants_with_prefix(prefix, restaurants):
- out = []
- for r in restaurants:
- if re.search(prefix, r[4][0]):
- out.append(r)
- return get_restaurants_sorted(out)
-
- tty_title = "TTY:n ruokalistat"
- tty = get_restaurants_with_prefix("TTY", unordered)
- write_all_days(tty, "tty/", tty_title, "../")
- write_table(tty, "tty/", tty_title, "../")
-
- tay_title = "Tampereen yliopiston ruokalistat"
- tay = get_restaurants_with_prefix("TaY", unordered)
- write_all_days(tay, "tay/", tay_title, "../")
- write_table(tay, "tay/", tay_title, "../")
-
- tays_title = "TAYS:n ruokalistat"
- tays = get_restaurants_with_prefix("TAYS", unordered)
- write_all_days(tays, "tays/", tays_title, "../")
- write_table(tays, "tays/", tays_title, "../")
-
- for r in unordered:
- if re.search(r"^\(TaY\)", r[0]):
- r[4][3] = "left"
- if re.search(r"^\(TTY\)", r[0]):
- r[4][3] = "right"
- if re.search(r"^\(TAYS\)", r[0]):
- r[4][3] = "middle"
-
- all_title = "Tampereen yliopistojen ruokalistat";
- all_restaurants = get_restaurants_sorted(unordered);
- # move fusion kitchen last
- #fusion = splice(@all_restaurants, 1, 1);
- #splice(@all_restaurants, 4, 0, @fusion);
-
- write_all_days(all_restaurants, "", all_title, "");
- write_table(all_restaurants, "", all_title, "");
|