Skip to content

Instantly share code, notes, and snippets.

@umutozd
Last active May 16, 2023 07:56
Show Gist options
  • Save umutozd/3c416a5b242e5b3c69f169161a07b6b5 to your computer and use it in GitHub Desktop.
Save umutozd/3c416a5b242e5b3c69f169161a07b6b5 to your computer and use it in GitHub Desktop.
Transparent Restaurant - Fullstack Task

Table of Contents

Background

There is a restaurant called Transparent Restaurant. 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

Due to the instability in the economy, the prices increase about every two weeks. This is a problem for the business because restaurant needs to print out new menu books each time the prices are updated, which is a significant cost in long term. Therefore, the restaurant owners have decided to go digital. They want to replace the physical menus with digital ones and have hired you to develop this system.

Technical Details

Because you work alone, you need to build both the backend and the frontend of this system. The backend shall be responsible of serving the menu to the frontend and the frontend shall be responsible of processing the menu and displaying it to the customers.

Backend Features and Requirements

Below are the detailed descriptions of the required and the bonus HTTP endpoints to be implemented by the backend. A few notes about the descriptions:

  • The sample responses are only for examplification and do not represent actual data. However, they do represent the required data format.
  • For all GET methods, the parameters must be in the URL query.
  • For all POST methods, the parameters must be in the request body. The body can consist of either JSON or url-encoded form data. You can choose either (or both) of them.
  • Parameters can either be required or optional.
  • Parameters can have default values. These default values, if specified, are generally programmatically-default values (e.g. false for boolean) and must be used if incoming requests do not contain other values.
  • All responses must be in JSON format.
  • The server must be running locally. HTTPS is not required.

Required Endpoints

GET /listMeals

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)

Parameters

Parameter Type Required Default Description
is_vegetarian boolean false false When true, non-vegetarian meals must be discarded.
is_vegan boolean false false When true, non-vegan meals must be discarded.

Example request-response

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"]
  }
]

GET /getMeal

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

Parameters

Parameter Type Required Default Description
id integer true The unique ID of the meal.

Example request-response

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
        },
        {
          "name": "Medium grain brown rice",
          "quality": "medium",
          "price": 2
        },
        {
          "name": "Quick cooking white rice",
          "quality": "low",
          "price": 1.5
        }
      ]
    },
    {
      "name": "Pasta",
      "groups": ["vegetarian"],
      "options": [
        {
          "name": "Semolina pasta",
          "quality": "high",
          "price": 2
        },
        {
          "name": "Whole wheat pasta",
          "quality": "medium",
          "price": 1.5
        },
        {
          "name": "Enriched pasta",
          "quality": "low",
          "price": 1
        }
      ]
    }
  ]
}

POST /quality

This endpoint takes a meal id with 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.

Parameters

Each <ingredient-n> can be any one of the ingredients in the meal. If meal doesn't have that ingredient, than it should be ignored.

Parameter Type Required Default Description
meal_id integer true The unique ID of the meal.
<ingredient-n> enum("high", "medium", "low) true Name of the ingredient in the meal and its desired quality value.

Example request-response

curl "http://localhost:8080/getMeal?id=2&garlic=high"
{
  "quality": 30
}

POST /price

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

Parameters

Each <ingredient-n> can be any one of the ingredients in the meal. If meal doesn't have that ingredient, than it should be ignored.

Parameter Type Required Default Description
meal_id integer true The unique ID of the meal.
<ingredient-n> enum("high", "medium", "low) true Name of the ingredient in the meal and its desired quality value.

Example request-response

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

Bonus Endpoints

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

POST /random

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

Parameters

Parameter Type Required Default Description
budget double false The maximum price of the generated meal. If not specified, there is no maximum.

Example request-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"
    }
  ]
}

GET /search

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

Parameters

Parameter Type Required Default Description
query string true The search string

Example request-response

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

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 shall set a proper status code and return a JSON response body that states the error correctly (an error with message Something went wrong is discouraged).

Frontend Features and Requirements

Core Feautures

In the website that you are going to build, the core actions that customers will be able to perform are:

  • View a list of the meals in the menu.
  • Filter for vegetarian or vegan meals in the menu.
  • Get details of a single meal.
  • Play with the quality levels of each ingredient in a meal and dynamically see the price and quality score of that selection.

The website shall:

  • not parse any dataset file. It must fetch the menu from the locally-running backend server
  • make use of /quality and /price endpoints in the backend when calculating the prices and quality scores.

Bonus Features

In addition to these, below are bonus features that you can choose to implement to get extra points.

  • Random meal selection for a given budget (aka. Kendimi şanslı hissediyorum :) )
  • Sorting the menu items based on attributes such as name, average price etc.. You can be creative and add more sorting options.

There are many points of extensions to this system, so feel free to do it your own way. You may add your own bonus features as long as the main requirements are completed. There are no restrictions in terms of the UI of your application. Feel free to be creative :)

Possible Pages

Some sample pages of the application are listed below as recommendation. As stated above, you may create your application UI as you like, you don't need to stick to these sample pages as long as the main requirements are satisfied.

1) Welcome Page

A page that welcomes the user and describes the restaurant, its methods and purposes briefly.

2) Menu Page

This page can include the list of meals. A rough sample design is shown below.

menu

3) Meal Page

This page can include the details of the meals. A rough simple design is shown below.

meal

Error Handling

If in any case your code encounters an error, a proper error message should be displayed on the screen (an error with message Something went wrong is discouraged).

Appendix

Dataset

The backend server shall 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.

An ingredient is vegetarian if it contains one of the groups vegetarian or vegan and an ingredient is vegan if it contains the group vegan.

Quality and Price Calculation

Quality Calculation

  • 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

For each ingredient in a meal with N used amount, add N * <unit_price> to the overall cost.

Example quality and price calculation:

  • For instance, Grilled chicken with roasted vegetables has two ingredients; Chicken and Vegetables.
  • For this meal, we need to use 3 units of Chicken and 4 units of Vegetables.
  • Say, we choose medium-quality chicken and high-quality vegetables, which cost $4 and $5 per unit.
  • The quality of the meal would then be (20 + 30) / 2 = 25 over 30 points (assuming that we use the above example quality scores, you can use your own grading).
  • The price of the used Chicken would be 3 * 4 = 12 and the price of the used Vegetables would be 5 * 4 = 20.
  • The overall price would be the sum of both the above prices, which is 12 + 20 = $32.

Rules and Submission

README

Provide README file explaining how to run your code. You should also explain the features you have implemented.

Git

You must put your implementation in a private repositories on GitHub or GitLab. Name it <name>-<surname>-otsimo-fullstack-2023. Both the backend and the frontend implementations must be in this repository, but should be separate from each other.

Backend

For backend, you may use only Go, Python or Node.js. You must stick to built-in libraries for the backend as they are more than capable for this task.

Frontend

For frontend, you may use any framework or CSS stylesheet you like. TypeScript usage is recommended, but not mandatory.

Submission

  • Until you finish your implementation, do not send us the link to your repository and do not add the collaborators described below.

  • Once you finish your implementation, send us 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.

  • GitHub:

  • GitLab:

Dataset Content

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

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