Skip to content

Instantly share code, notes, and snippets.

@sercand
Last active May 30, 2024 12:02
Show Gist options
  • Save sercand/1a62eb96c252b3bc343f277162f20a8b to your computer and use it in GitHub Desktop.
Save sercand/1a62eb96c252b3bc343f277162f20a8b to your computer and use it in GitHub Desktop.

Transparent Restaurant Backend

In this task, you're going to implement a REST API for a interacting with a menu of a restaurant. The menu is given to you as a JSON file which you will parse and perform operations on. The required features will be listed below.

Description

In this restaurant, honesty is extremely promoted. So extreme, that the restaurant declares that differing quality of ingredients are used in their meals. Like that's not enough, it also allows the customers to choose the ingredients of each meal in different qualities. Each ingredient has the following quality levels:

  • low: the cheapest
  • medium: moderate
  • high: the most expensive

Main Features and Requirements

The restaurant owners take pride in their perspective of transparency. However, they are worried that taking orders from the customers may take too long for a handful of waiters to catch up to all customers. Therefore, they require a system where customers can:

  • View a list of the menu with the following filtering and sorting options:
  • Get details of a single meal
  • Price and quality-score calculation for a given set of quality parameters

HTTP API

You are required to implement only 5 endpoints for this task, which are listed below but are encouraged to implement the bonus ones as well. The server should take a dataset as a JSON file and parse it before launch. The dataset is at the end of this text.

The sample responses in the following sections are only for examplification and do not represent actual data.

Listing the Menu

Lists the meals in the menu and their ingredients. Options to filter for vegetarian or vegan meals must be implemented as well (see Vegetarian and Vegan Definition)

PATH: /listMeals
METHOD: GET
PARAMS:
  is_vegetarian: (boolean, optional) default=false
  is_vegan: (boolean, optional) default=false
SAMPLE: http://localhost:8080/listMeals?is_vegetarian=true

Example JSON output:

$ curl http://localhost:8080/listMeals
[
  {
    "id": 1,
    "name": "Shrimp-fried Rice",
    "ingredients": [
      "shrimp",
      "rice"
    ]
  },
  {
    "id": 2,
    "name": "Beer Plate",
    "ingredients": [
      "French Fries",
      "Onion Rings",
      "Sausages",
      "Chicken Wings"
    ]
  }
]

Getting an Item from Menu

This endpoint takes a meal ID and returns its name and ingredients with each option of ingredients included.

PATH: /getMeal
METHOD: GET
PARAMS:
    id: N (integer, required)
SAMPLE: http://localhost:8080/getMeal?id=2

Example JSON output:

$ curl http://localhost:8080/getMeal?id=2
{
  "id": 2,
  "name": "Beer Plate",
  "ingredients": [
    {
      "name": "Rice",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Long grain white rice",
          "quality": "high",
          "price": 3,
          "per_amount": "kilogram"
        },
        {
          "name": "Medium grain brown rice",
          "quality": "medium",
          "price": 2,
          "per_amount": "kilogram"
        },
        {
          "name": "Quick cooking white rice",
          "quality": "low",
          "price": 1.5,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Pasta",
      "groups": ["vegetarian"],
      "options": [
        {
          "name": "Semolina pasta",
          "quality": "high",
          "price": 2,
          "per_amount": "kilogram"
        },
        {
          "name": "Whole wheat pasta",
          "quality": "medium",
          "price": 1.5,
          "per_amount": "kilogram"
        },
        {
          "name": "Enriched pasta",
          "quality": "low",
          "price": 1,
          "per_amount": "kilogram"
        }
      ]
    }
  ]
}

Quality Calculation With Ingredient Qualities

This endpoint takes a meal id with all of its ingredients' quality selecitons and returns the resulting quality score. If an ingredient's quality is not specified, "high" quality should be assumed by default.

PATH: /quality
METHOD: POST
PARAMS:
  meal_id: (integer, required)
  <ingredient-1>: (enum, values: ["high", "medium", "low"], optional) default="high"
  <ingredient-2>: (enum, values: ["high", "medium", "low"], optional) default="high"
  ...

Example JSON output:

$ curl -d "meal_id=3&garlic=high" -X POST http://localhost:8080/quality
{
  "quality": 30
}

Price Calculation With Ingredient Qualities

This endpoint takes a meal id with all of its ingredients' quality selecitons and returns the resulting price. If an ingredient's quality is not specified, "high" quality should be assumed by default.

PATH: /price
METHOD: POST
PARAMS:
  meal_id: (integer, required)
  <ingredient-1>: (enum, values: ["high", "medium", "low"], optional) default="high"
  <ingredient-2>: (enum, values: ["high", "medium", "low"], optional) default="high"
  ...

Example JSON output:

$ curl -d "meal_id=3&garlic=low" -X POST http://localhost:8080/price
{
  "price": 40.99
}

I'm Feeling Lucky

This endpoint returns a randomly selected meal of random quality parameters, with an option to set a budget.

PATH: /random
METHOD: POST
PARAMS:
  budget: (double, optional) default=unlimited

Example JSON response:

$ curl -d "budget=42.42" -X POST http://localhost:8080/random
{
  "id": 2,
  "name": "Beer Plate",
  "price": 40.99,
  "quality_score": 27,
  "ingredients": [
    {
      "name": "French Fries",
      "quality": "high"
    },
    {
      "name": "Onion Rings",
      "quality": "high"
    },
    {
      "name": "Sausages",
      "quality": "high"
    },
    {
      "name": "Chicken Wings",
      "quality": "low"
    }
  ]
}

API Errors

If in any case the API needs to return an error (e.g. when an invalid id is passed to /getMeal endpoint), it should do so by conforming to the HTTP standard and should return a JSON body that states the error properly (an error with message Something went wrong is discouraged).

Bonus Endpoints

These endpoints are optional but will get you a higher score during the evalutaion phase should you implement them.

Searching For a Meal

This endpoint takes a search text and returns the meals that contain the search text. Search will be made in a case-insensitive manner.

PATH: /search
METHOD: GET
PARAMS:
  query: (string, required)

Example JSON output:

$ curl http://localhost:8080/search?query=beer
[
  {
    "id": 2,
    "name": "Beer Plate",
    "ingredients": [
      "French Fries",
      "Onion Rings",
      "Sausages",
      "Chicken Wings"
    ]
  }
]

Finding the Highest Quality Meal For Given Budget

This endpoint takes a budget as input and yields the highest-quality meal that can be prepared for that budget and how much it costs.

PATH: /findHighest
METHOD: POST
PARAMS:
  budget: (double, required)
  is_vegetarian: (boolean, optional) default=false
  is_vegan: (boolean, optional) default=false

Example JSON output:

$ curl -d "budget=42.42&is_vegetarian=false&is_vegan=false" -X POST http://localhost:8080/findHighest
{
  "id": 2,
  "name": "Beer Plate",
  "price": 40.99,
  "quality_score": 27,
  "ingredients": [
    {
      "name": "French Fries",
      "quality": "high"
    },
    {
      "name": "Onion Rings",
      "quality": "high"
    },
    {
      "name": "Sausages",
      "quality": "high"
    },
    {
      "name": "Chicken Wings",
      "quality": "low"
    }
  ]
}

Finding the Highest Quality Version of a Meal For Given Budget

This endpoint takes a budget and meal id as input and yields the highest-quality version of it that can be prepared for that budget and how much it costs.

PATH: /findHighestOfMeal
METHOD: POST
PARAMS:
  meal_id: (integer, required)
  budget: (double, required)

Example JSON output:

$ curl -d "budget=42.42&meal_id=2&is_vegan=false" -X POST http://localhost:8080/findHighestOfMeal
{
  "id": 2,
  "name": "Beer Plate",
  "price": 40.99,
  "quality_score": 27,
  "ingredients": [
    {
      "name": "French Fries",
      "quality": "high"
    },
    {
      "name": "Onion Rings",
      "quality": "high"
    },
    {
      "name": "Sausages",
      "quality": "high"
    },
    {
      "name": "Chicken Wings",
      "quality": "low"
    }
  ]
}

Dataset

Your code must parse the given dataset file, that is in JSON format, and use it as the source of information in further operations. This dataset is provided by us with all information about the restaurant's menu, the ingredients, their quality and their prices. You should not base your implementation on the exact data in this dataset as we will use a different one during evaluation, but the data structure will be the same.

Vegetarian and Vegan Definition

In this system, a vegetarian meal is one that contains only vegetarian or vegan ingredients and a vegan meal is one that contains only vegan ingredients.

Quality and Price Calculation

Quality calculation is performed in the following way:

  • Each quality level has a corresponding score (e.g. low->10, medium->20, high->30, you may use your own values).
  • The scores of each ingredient used in a meal are summed and divided to the number of ingredients to find the overall score of the meal.
  • This overall score represents the quality of the meal.

Price calculation is performed in the following way:

  • Prices of all ingredients used in the meal are summed together.
  • In addition, each degraded level of ingredient adds a $0.05 cost to the restaurant as it becomes harder prepare a meal with a lower-quality ingredient.
    • For instance, using a medium-quality ingredient results in a $0.05 extra cost while low-quality results in a $0.10 extra cost.

An example quality and price calculation:

  • For instance, say Rice and chicken bowl has 120 grams rice and 85 grams chicken as ingredients.
  • Say, low-quality rice and high-quality chicken are used, which correspond to $1.5 and $10 for a kilogram respectively.
  • Low-quality rice means two levels of degredation, which adds $0.10 to the overall cost.
  • For 120 grams rice, the cost is (120 / 1000)*1.5 = $0.18
  • For 85 grams chicken, the cost is (85 / 1000)*10 = $0.85
  • Total cost becomes 0.18 + 0.85 + 0.10 = $1.13
  • Overall quality of the food becomes (30 + 10) / 2 = 20 out of 30, assuming low and high quality correspond to 10 and 30 scores, respectively.

Additional Notes and Rules

  • Don't forget to provide README file explaining how to run your code. You should also explain the features you have implemented.
  • You must put your implementation in a private repository on GitHub or GitLab and name it <name>-<surname>-otsimo-internship-task-2024.
  • You must use Go or Python to create your application.
  • In either of the languages, you must stick to built-in libraries, they are quite enough for this task (i.e. do not use Flask or Django etc.).
  • Once you finish your implementation, you must send an e-mail informing that you've completed your task and include the link to the repository. Add all of the following collaborators to your repository depending on your platform. Add the collaborators only after you finished the task.

Dataset Content

Expand to view the full dataset file
{
  "meals": [
    {
      "id": 1,
      "name": "Rice and chicken bowl",
      "ingredients": [
        { "name": "Rice", "quantity": 120, "quantity_type": "gram" },
        { "name": "Chicken", "quantity": 85, "quantity_type": "gram" }
      ]
    },
    {
      "id": 2,
      "name": "Pasta with marinara sauce and vegetables",
      "ingredients": [
        { "name": "Pasta", "quantity": 115, "quantity_type": "gram" },
        {
          "name": "Marinara sauce",
          "quantity": 120,
          "quantity_type": "millilitre"
        },
        { "name": "Vegetables", "quantity": 240, "quantity_type": "gram" }
      ]
    },
    {
      "id": 3,
      "name": "Grilled chicken with roasted vegetables",
      "ingredients": [
        { "name": "Chicken", "quantity": 85, "quantity_type": "gram" },
        { "name": "Vegetables", "quantity": 240, "quantity_type": "gram" }
      ]
    },
    {
      "id": 4,
      "name": "Beef stir-fry with rice",
      "ingredients": [
        { "name": "Beef", "quantity": 115, "quantity_type": "gram" },
        { "name": "Rice", "quantity": 120, "quantity_type": "gram" },
        { "name": "Vegetables", "quantity": 240, "quantity_type": "gram" }
      ]
    },
    {
      "id": 5,
      "name": "Pork chops with mashed potatoes and gravy",
      "ingredients": [
        { "name": "Pork chops", "quantity": 115, "quantity_type": "gram" },
        {
          "name": "Mashed potatoes",
          "quantity": 120,
          "quantity_type": "gram"
        },
        { "name": "Gravy", "quantity": 120, "quantity_type": "millilitre" }
      ]
    },
    {
      "id": 6,
      "name": "Grilled salmon with roasted asparagus",
      "ingredients": [
        { "name": "Salmon", "quantity": 85, "quantity_type": "gram" },
        { "name": "Asparagus", "quantity": 240, "quantity_type": "gram" }
      ]
    },
    {
      "id": 7,
      "name": "Shrimp scampi with linguine",
      "ingredients": [
        { "name": "Shrimp", "quantity": 115, "quantity_type": "gram" },
        { "name": "Linguine", "quantity": 115, "quantity_type": "gram" },
        { "name": "Butter", "quantity": 10, "quantity_type": "millilitre" },
        { "name": "Garlic", "quantity": 10, "quantity_type": "gram" },
        { "name": "White wine", "quantity": 60, "quantity_type": "millilitre" }
      ]
    },
    {
      "id": 8,
      "name": "Vegetarian stir-fry with tofu",
      "ingredients": [
        { "name": "Tofu", "quantity": 115, "quantity_type": "gram" },
        { "name": "Rice", "quantity": 120, "quantity_type": "gram" },
        { "name": "Vegetables", "quantity": 240, "quantity_type": "gram" }
      ]
    },
    {
      "id": 9,
      "name": "Fruit salad with mixed berries and yogurt",
      "ingredients": [
        { "name": "Mixed berries", "quantity": 240, "quantity_type": "gram" },
        { "name": "Yogurt", "quantity": 120, "quantity_type": "millilitre" }
      ]
    }
  ],

  "ingredients": [
    {
      "name": "Rice",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Long grain white rice",
          "quality": "high",
          "price": 3,
          "per_amount": "kilogram"
        },
        {
          "name": "Medium grain brown rice",
          "quality": "medium",
          "price": 2,
          "per_amount": "kilogram"
        },
        {
          "name": "Quick cooking white rice",
          "quality": "low",
          "price": 1.5,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Pasta",
      "groups": ["vegetarian"],
      "options": [
        {
          "name": "Semolina pasta",
          "quality": "high",
          "price": 2,
          "per_amount": "kilogram"
        },
        {
          "name": "Whole wheat pasta",
          "quality": "medium",
          "price": 1.5,
          "per_amount": "kilogram"
        },
        {
          "name": "Enriched pasta",
          "quality": "low",
          "price": 1,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Chicken",
      "groups": [],
      "options": [
        {
          "name": "Organic, free-range chicken",
          "quality": "high",
          "price": 10,
          "per_amount": "kilogram"
        },
        {
          "name": "Conventional chicken",
          "quality": "medium",
          "price": 7,
          "per_amount": "kilogram"
        },
        {
          "name": "Frozen chicken",
          "quality": "low",
          "price": 4,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Beef",
      "groups": [],
      "options": [
        {
          "name": "Grass-fed beef",
          "quality": "high",
          "price": 16,
          "per_amount": "kilogram"
        },
        {
          "name": "Grain-fed beef",
          "quality": "medium",
          "price": 12,
          "per_amount": "kilogram"
        },
        {
          "name": "Processed beef",
          "quality": "low",
          "price": 8,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Pork",
      "groups": [],
      "options": [
        {
          "name": "Heritage breed pork",
          "quality": "high",
          "price": 12,
          "per_amount": "kilogram"
        },
        {
          "name": "Conventional pork",
          "quality": "medium",
          "price": 9,
          "per_amount": "kilogram"
        },
        {
          "name": "Processed pork",
          "quality": "low",
          "price": 6,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Salmon",
      "groups": [],
      "options": [
        {
          "name": "Wild-caught salmon",
          "quality": "high",
          "price": 24,
          "per_amount": "kilogram"
        },
        {
          "name": "Farmed salmon",
          "quality": "medium",
          "price": 16,
          "per_amount": "kilogram"
        },
        {
          "name": "Canned tuna",
          "quality": "low",
          "price": 8,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Shrimp",
      "groups": [],
      "options": [
        {
          "name": "Wild-caught shrimp",
          "quality": "high",
          "price": 20,
          "per_amount": "kilogram"
        },
        {
          "name": "Farm-raised shrimp",
          "quality": "medium",
          "price": 15,
          "per_amount": "kilogram"
        },
        {
          "name": "Frozen shrimp",
          "quality": "low",
          "price": 10,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Vegetables",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Fresh, organic vegetables",
          "quality": "high",
          "price": 8,
          "per_amount": "kilogram"
        },
        {
          "name": "Fresh, conventional vegetables",
          "quality": "medium",
          "price": 5,
          "per_amount": "kilogram"
        },
        {
          "name": "Frozen vegetables",
          "quality": "low",
          "price": 3,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Fruit",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Fresh, organic fruit",
          "quality": "high",
          "price": 6,
          "per_amount": "kilogram"
        },
        {
          "name": "Fresh, conventional fruit",
          "quality": "medium",
          "price": 4,
          "per_amount": "kilogram"
        },
        {
          "name": "Canned fruit",
          "quality": "low",
          "price": 2,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Dairy",
      "groups": ["vegetarian"],
      "options": [
        {
          "name": "Organic, grass-fed dairy",
          "quality": "high",
          "price": 16,
          "per_amount": "litre"
        },
        {
          "name": "Conventional dairy",
          "quality": "medium",
          "price": 8,
          "per_amount": "litre"
        },
        {
          "name": "Processed dairy",
          "quality": "low",
          "price": 4,
          "per_amount": "litre"
        }
      ]
    },
    {
      "name": "Marinara sauce",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Homemade marinara sauce",
          "quality": "high",
          "price": 20,
          "per_amount": "litre"
        },
        {
          "name": "Store-bought marinara sauce",
          "quality": "medium",
          "price": 12,
          "per_amount": "litre"
        },
        {
          "name": "Canned marinara sauce",
          "quality": "low",
          "price": 6,
          "per_amount": "litre"
        }
      ]
    },
    {
      "name": "Butter",
      "groups": ["vegetarian"],
      "options": [
        {
          "name": "Grass-fed butter",
          "quality": "high",
          "price": 12,
          "per_amount": "kilogram"
        },
        {
          "name": "Conventional butter",
          "quality": "medium",
          "price": 8,
          "per_amount": "kilogram"
        },
        {
          "name": "Margarine",
          "quality": "low",
          "price": 4,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Garlic",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Fresh, organic garlic",
          "quality": "high",
          "price": 6,
          "per_amount": "kilogram"
        },
        {
          "name": "Fresh, conventional garlic",
          "quality": "medium",
          "price": 4,
          "per_amount": "kilogram"
        },
        {
          "name": "Frozen garlic",
          "quality": "low",
          "price": 2,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "White wine",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "High-end white wine",
          "quality": "high",
          "price": 40,
          "per_amount": "litre"
        },
        {
          "name": "Mid-range white wine",
          "quality": "medium",
          "price": 30,
          "per_amount": "litre"
        },
        {
          "name": "Cheap white wine",
          "quality": "low",
          "price": 20,
          "per_amount": "litre"
        }
      ]
    },
    {
      "name": "Mashed potatoes",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Homemade mashed potatoes",
          "quality": "high",
          "price": 10,
          "per_amount": "gram"
        },
        {
          "name": "Store-bought mashed potatoes",
          "quality": "medium",
          "price": 7,
          "per_amount": "gram"
        },
        {
          "name": "Instant mashed potatoes",
          "quality": "low",
          "price": 4,
          "per_amount": "gram"
        }
      ]
    },
    {
      "name": "Gravy",
      "groups": [],
      "options": [
        {
          "name": "Homemade gravy",
          "quality": "high",
          "price": 10,
          "per_amount": "litre"
        },
        {
          "name": "Store-bought gravy",
          "quality": "medium",
          "price": 7,
          "per_amount": "litre"
        },
        {
          "name": "Instant gravy",
          "quality": "low",
          "price": 4,
          "per_amount": "litre"
        }
      ]
    },
    {
      "name": "Asparagus",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Fresh, organic asparagus",
          "quality": "high",
          "price": 8,
          "per_amount": "kilogram"
        },
        {
          "name": "Fresh, conventional asparagus",
          "quality": "medium",
          "price": 5,
          "per_amount": "kilogram"
        },
        {
          "name": "Frozen asparagus",
          "quality": "low",
          "price": 3,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Tofu",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "High-quality tofu",
          "quality": "high",
          "price": 8,
          "per_amount": "kilogram"
        },
        {
          "name": "Medium-quality tofu",
          "quality": "medium",
          "price": 6,
          "per_amount": "kilogram"
        },
        {
          "name": "Low-quality tofu",
          "quality": "low",
          "price": 4,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Yogurt",
      "groups": ["vegetarian"],
      "options": [
        {
          "name": "Organic, grass-fed yogurt",
          "quality": "high",
          "price": 12,
          "per_amount": "litre"
        },
        {
          "name": "Conventional yogurt",
          "quality": "medium",
          "price": 8,
          "per_amount": "litre"
        },
        {
          "name": "Processed yogurt",
          "quality": "low",
          "price": 4,
          "per_amount": "litre"
        }
      ]
    },
    {
      "name": "Mixed berries",
      "groups": ["vegan", "vegetarian"],
      "options": [
        {
          "name": "Fresh, organic mixed berries",
          "quality": "high",
          "price": 12,
          "per_amount": "kilogram"
        },
        {
          "name": "Fresh, conventional mixed berries",
          "quality": "medium",
          "price": 8,
          "per_amount": "kilogram"
        },
        {
          "name": "Frozen mixed berries",
          "quality": "low",
          "price": 4,
          "per_amount": "kilogram"
        }
      ]
    },
    {
      "name": "Linguine",
      "groups": ["vegetarian"],
      "options": [
        {
          "name": "High-end linguine",
          "quality": "high",
          "price": 2,
          "per_amount": "kilogram"
        },
        {
          "name": "Mid-range linguine",
          "quality": "medium",
          "price": 1.5,
          "per_amount": "kilogram"
        },
        {
          "name": "Cheap linguine",
          "quality": "low",
          "price": 1,
          "per_amount": "kilogram"
        }
      ]
    }
  ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment