#!/usr/bin/env python3

import sys
import argparse
import json

import requests
from bs4 import BeautifulSoup
import progressbar as pb
from termcolor import colored


BASE_URL = "https://www.studentska-prehrana.si/"
RESTAURANT_EXT = "sl/restaurant"


class Restaurant():
    def __init__(self):
        self.name = ""
        self.longitude = 0.0
        self.latitude = 0.0
        self.address = ""
        self.price_student = 0.0
        self.price_full = 0.0
        self.city = ""
        self.meals = []

        self.vegi = False
        self.weekends = False
        self.for_disabled = False
        self.delivery = False
        self.lunch = False

    def __repr__(self):
        text = "{} {}".format(colored(self.name, attrs=["bold", "underline"]),
                              colored("({})".format(self.city), attrs=["dark"]))

        if self.vegi:         text += colored(" [VEGGIE]", "yellow")
        if self.weekends:     text += colored(" [WEEKEND]", "yellow")
        if self.for_disabled: text += colored(" [WHEELCHAIR]", "yellow")
        if self.delivery:     text += colored(" [DELIVERY]", "yellow")
        if self.lunch:        text += colored(" [LUNCH]", "yellow")

        text += " => {} {}".format(colored("{}€".format(self.price_student), attrs=["bold"]),
                                   colored("({}€)".format(self.price_full), attrs=["dark"]))

        for meal in self.meals:
            text += "\n- {}".format(meal)

        return text

    @classmethod
    def from_html(cls, element):
        # Retrieve restaurant data from tags
        restaurant = cls()

        try:
            restaurant.latitude = float(element["data-lon"].replace(",", "."))
            restaurant.longitude = float(element["data-lat"].replace(",", "."))
        except ValueError:
            pass

        restaurant.address = element["data-naslov"]
        try:
            restaurant.price_full = float(element["data-cena"].replace(",", "."))
            restaurant.price_student = float(element["data-doplacilo"].replace(",", "."))
        except ValueError:
            pass
        restaurant.name = element["data-lokal"]
        restaurant.city = element["data-city"]

        # Check for extra info using icons and their tooltips
        extras = element.find_all("img")
        for extra in extras:
            if extra["title"] == "Vegetarijansko":
                restaurant.vegi = True
            elif extra["title"] == "Dostop za invalide":
                restaurant.for_disabled = True
            elif extra["title"] == "Odprt ob vikendih":
                restaurant.weekends = True
            elif extra["title"] == "Dostava":
                restaurant.delivery = True
            elif extra["title"] == "Kosilo":
                restaurant.lunch = True

        return restaurant


class Meal():
    def __init__(self):
        self.main_meal = ""
        self.extras = []

    def __repr__(self):
        return "{} {}".format(self.main_meal,
                              colored("({})".format(", ".join(self.extras)), attrs=["dark"]))

    @classmethod
    def from_html(cls, element):
        meal = cls()
        temp = element.find_all("p")[0].find_all("h5")[0]

        # Get main meal (split by spaces to get rid of leading number)
        meal.main_meal = temp.find_all("strong")[0].get_text().split("  ")[1]

        # Retrieve extras
        extras = element.find_all("ul")[0].find_all("li")
        for extra in extras:
            extra_food = extra.find_all("i")[0].get_text()
            if extra_food:
                if len(extra_food) > 0 and "&nbsp" not in extra_food:
                    meal.extras.append(extra_food.strip())

        return meal


def main():
    # Parse arguments
    parser = argparse.ArgumentParser(
        description="Search restaurants providing student food on {}".format(BASE_URL))
    parser.add_argument("-m", "--meals", action="store_true", help="show meals")
    parser.add_argument("-a", "--address", action="store_true", help="show address")
    parser.add_argument("-n", "--name", type=str, default="", help="name to search by")
    parser.add_argument("-c", "--city", type=str, default="", help="city to search in")
    parser.add_argument("-v", "--vegetarian", action="store_true", help="require vegetarian offer")
    parser.add_argument("-w", "--weekends", action="store_true", help="require weekend offer")
    parser.add_argument("--wheelchair", action="store_true", help="require wheelchair entrance")
    parser.add_argument("-d", "--delivery", action="store_true", help="require delivery service")
    parser.add_argument("-l", "--lunch", action="store_true", help="require lunch")
    parser.add_argument("-p", "--price", type=float, default=-1.0, help="require price to be same as given value")
    parser.add_argument("--price-le", type=float, default=float("inf"), help="require price to be lower than given value")
    parser.add_argument("--price-ge", type=float, default=0.0, help="require price to be higher than given value")
    parser.add_argument("-o", "--out", type=str, help="write to given file (json)")

    args = parser.parse_args();
    args.name = args.name.lower().strip()
    args.city = args.city.lower().strip()

    # Search
    restaurants = []
    try:
        # Get html from resource, parse it and get all restaurant elements
        html = requests.get("{}/{}".format(BASE_URL, RESTAURANT_EXT))
        document = BeautifulSoup(html.content, 'html.parser')
        restaurants = document.find_all(class_="restaurant-row")
    except Exception as e:
        print("Failed to fetch restaurants! {}".format(e))
        return 1

    found = []

    # Retrieve data
    print("Searching restaurants on {}...".format(BASE_URL))
    for element in pb.progressbar(restaurants):
        # Parse restaurant basic data
        restaurant = Restaurant.from_html(element)

        # Conditions
        if args.name and args.name not in restaurant.name.lower().strip(): continue
        if args.city and args.city not in restaurant.city.lower().strip(): continue
        if args.vegetarian and not restaurant.vegi:         continue
        if args.weekends and not restaurant.weekends:       continue
        if args.wheelchair and not restaurant.for_disabled: continue
        if args.delivery and not restaurant.delivery:       continue
        if args.lunch and not restaurant.lunch:             continue
        if args.price >= 0.0 and args.price != restaurant.price_student: continue
        if restaurant.price_student > args.price_le:                     continue
        if restaurant.price_student < args.price_ge:                     continue


        found.append(restaurant)

        # Parse restaurant details - ignore if no details provided
        if not args.meals:
            continue

        source = element["data-detailslink"]
        if not source or len(source.strip()) <= 0:
            continue

        # Get html from resource and parse it
        url = "{}/{}".format(BASE_URL, source)
        html = requests.get(url)
        document = BeautifulSoup(html.content, 'html.parser')

        # Get meal elements
        meals = document.find(id="menu-list")
        for entry in meals.find_all(class_="shadow-wrapper"):
            try:
                restaurant.meals.append(Meal.from_html(entry))
            except Exception as e:
                pass  # No menu/meal defined

    # Pretty print
    if not args.meals:
        print()

    for f in found:
        if args.meals:
            print()

        print(f)
        if args.address:
            print(colored("[{} ({}, {})]".format(f.address, f.longitude, f.latitude), attrs=["dark"]))

    print("\nFound {} restaurants!".format(len(found)))

    # Write to file
    if args.out:
        with open(args.out, "w", newline="\n", encoding="utf-8") as f:
            json.dump(found, f, default=lambda o: o.__dict__, indent=2)

    return 0


if __name__ == "__main__":
    sys.exit(main())

#!/usr/bin/env python3

import sys
import argparse
import json

import requests
from bs4 import BeautifulSoup
import progressbar as pb
from termcolor import colored


BASE_URL = "https://www.studentska-prehrana.si/"
RESTAURANT_EXT = "sl/restaurant"


class Restaurant():
    def __init__(self):
        self.name = ""
        self.longitude = 0.0
        self.latitude = 0.0
        self.address = ""
        self.price_student = 0.0
        self.price_full = 0.0
        self.city = ""
        self.meals = []

        self.vegi = False
        self.weekends = False
        self.for_disabled = False
        self.delivery = False
        self.lunch = False

    def __repr__(self):
        text = "{} {}".format(colored(self.name, attrs=["bold", "underline"]),
                              colored("({})".format(self.city), attrs=["dark"]))

        if self.vegi:         text += colored(" [VEGGIE]", "yellow")
        if self.weekends:     text += colored(" [WEEKEND]", "yellow")
        if self.for_disabled: text += colored(" [WHEELCHAIR]", "yellow")
        if self.delivery:     text += colored(" [DELIVERY]", "yellow")
        if self.lunch:        text += colored(" [LUNCH]", "yellow")

        text += " => {} {}".format(colored("{}€".format(self.price_student), attrs=["bold"]),
                                   colored("({}€)".format(self.price_full), attrs=["dark"]))

        for meal in self.meals:
            text += "\n- {}".format(meal)

        return text

    @classmethod
    def from_html(cls, element):
        # Retrieve restaurant data from tags
        restaurant = cls()

        try:
            restaurant.latitude = float(element["data-lon"].replace(",", "."))
            restaurant.longitude = float(element["data-lat"].replace(",", "."))
        except ValueError:
            pass

        restaurant.address = element["data-naslov"]
        try:
            restaurant.price_full = float(element["data-cena"].replace(",", "."))
            restaurant.price_student = float(element["data-doplacilo"].replace(",", "."))
        except ValueError:
            pass
        restaurant.name = element["data-lokal"]
        restaurant.city = element["data-city"]

        # Check for extra info using icons and their tooltips
        extras = element.find_all("img")
        for extra in extras:
            if extra["title"] == "Vegetarijansko":
                restaurant.vegi = True
            elif extra["title"] == "Dostop za invalide":
                restaurant.for_disabled = True
            elif extra["title"] == "Odprt ob vikendih":
                restaurant.weekends = True
            elif extra["title"] == "Dostava":
                restaurant.delivery = True
            elif extra["title"] == "Kosilo":
                restaurant.lunch = True

        return restaurant


class Meal():
    def __init__(self):
        self.main_meal = ""
        self.extras = []

    def __repr__(self):
        return "{} {}".format(self.main_meal,
                              colored("({})".format(", ".join(self.extras)), attrs=["dark"]))

    @classmethod
    def from_html(cls, element):
        meal = cls()
        temp = element.find_all("p")[0].find_all("h5")[0]

        # Get main meal (split by spaces to get rid of leading number)
        meal.main_meal = temp.find_all("strong")[0].get_text().split("  ")[1]

        # Retrieve extras
        extras = element.find_all("ul")[0].find_all("li")
        for extra in extras:
            extra_food = extra.find_all("i")[0].get_text()
            if extra_food:
                if len(extra_food) > 0 and "&nbsp" not in extra_food:
                    meal.extras.append(extra_food.strip())

        return meal


def main():
    # Parse arguments
    parser = argparse.ArgumentParser(
        description="Search restaurants providing student food on {}".format(BASE_URL))
    parser.add_argument("-m", "--meals", action="store_true", help="show meals")
    parser.add_argument("-a", "--address", action="store_true", help="show address")
    parser.add_argument("-n", "--name", type=str, default="", help="name to search by")
    parser.add_argument("-c", "--city", type=str, default="", help="city to search in")
    parser.add_argument("-v", "--vegetarian", action="store_true", help="require vegetarian offer")
    parser.add_argument("-w", "--weekends", action="store_true", help="require weekend offer")
    parser.add_argument("--wheelchair", action="store_true", help="require wheelchair entrance")
    parser.add_argument("-d", "--delivery", action="store_true", help="require delivery service")
    parser.add_argument("-l", "--lunch", action="store_true", help="require lunch")
    parser.add_argument("-p", "--price", type=float, default=-1.0, help="require price to be same as given value")
    parser.add_argument("--price-le", type=float, default=float("inf"), help="require price to be lower than given value")
    parser.add_argument("--price-ge", type=float, default=0.0, help="require price to be higher than given value")
    parser.add_argument("-o", "--out", type=str, help="write to given file (json)")

    args = parser.parse_args();
    args.name = args.name.lower().strip()
    args.city = args.city.lower().strip()

    # Search
    restaurants = []
    try:
        # Get html from resource, parse it and get all restaurant elements
        html = requests.get("{}/{}".format(BASE_URL, RESTAURANT_EXT))
        document = BeautifulSoup(html.content, 'html.parser')
        restaurants = document.find_all(class_="restaurant-row")
    except Exception as e:
        print("Failed to fetch restaurants! {}".format(e))
        return 1

    found = []

    # Retrieve data
    print("Searching restaurants on {}...".format(BASE_URL))
    for element in pb.progressbar(restaurants):
        # Parse restaurant basic data
        restaurant = Restaurant.from_html(element)

        # Conditions
        if args.name and args.name not in restaurant.name.lower().strip(): continue
        if args.city and args.city not in restaurant.city.lower().strip(): continue
        if args.vegetarian and not restaurant.vegi:         continue
        if args.weekends and not restaurant.weekends:       continue
        if args.wheelchair and not restaurant.for_disabled: continue
        if args.delivery and not restaurant.delivery:       continue
        if args.lunch and not restaurant.lunch:             continue
        if args.price >= 0.0 and args.price != restaurant.price_student: continue
        if restaurant.price_student > args.price_le:                     continue
        if restaurant.price_student < args.price_ge:                     continue


        found.append(restaurant)

        # Parse restaurant details - ignore if no details provided
        if not args.meals:
            continue

        source = element["data-detailslink"]
        if not source or len(source.strip()) <= 0:
            continue

        # Get html from resource and parse it
        url = "{}/{}".format(BASE_URL, source)
        html = requests.get(url)
        document = BeautifulSoup(html.content, 'html.parser')

        # Get meal elements
        meals = document.find(id="menu-list")
        for entry in meals.find_all(class_="shadow-wrapper"):
            try:
                restaurant.meals.append(Meal.from_html(entry))
            except Exception as e:
                pass  # No menu/meal defined

    # Pretty print
    if not args.meals:
        print()

    for f in found:
        if args.meals:
            print()

        print(f)
        if args.address:
            print(colored("[{} ({}, {})]".format(f.address, f.longitude, f.latitude), attrs=["dark"]))

    print("\nFound {} restaurants!".format(len(found)))

    # Write to file
    if args.out:
        with open(args.out, "w", newline="\n", encoding="utf-8") as f:
            json.dump(found, f, default=lambda o: o.__dict__, indent=2)

    return 0


if __name__ == "__main__":
    sys.exit(main())

#!/usr/bin/env python3 # Retrieve restaurant data from tags # Check for extra info using icons and their tooltips # Get main meal (split by spaces to get rid of leading number) # Retrieve extras # Parse arguments # Search # Get html from resource, parse it and get all restaurant elements # Retrieve data # Parse restaurant basic data # Conditions # Parse restaurant details - ignore if no details provided # Get html from resource and parse it # Get meal elements # No menu/meal defined # Pretty print # Write to file