Splore Coding Assignment with Solution | Case Study

Splore Coding Assignment with Solution | Case Study

This coding assignment was asked to me for the Technical lead job position. Here I’m sharing a complete solution to this coding challenge.

Singa Metro Authority

The public transport system for city of Singa – Singa metro authority (SMA) wants to design a payment system for its service. They allow all banks credit and debit NFC-enabled cards to tap at entry and exit. They also sell NFC-enabled rechargeable SmartTap cards.

The metro system is divided into Lines named with different colors. There will be multiple stations on each line. The fare is calculated based on the Lines you are traveling. There can be more Lines and more stations coming in the future.

If you are preparing for job interviews for product-based companies, here is a list of coding challenges asked in the latest Interviews.

Problem statement

Design a fare calculation system for SMA. There are only two lines Red and Green. Fare is only based on the lines the user is traveling. To keep it simple, stations are not considered for the task. Consider all the fare rules.

Expected time: 6-8 hours

Input

FromLine, ToLine, DateTime
To keep it simple in your code, consider a CSV as input

Example

Green, Green, 2021-03-24T07:58:30
Green, Red, 2021-03-24T09:58:30
Red, Red, 2021-03-25T11:58:30

Output

Total fare applied

Rules

Fares

From Line To Line Peak Non Peak
Green Green $2 $1
Red Red $3 $2
Green Red $4 $3
Red Green $3 $2

Peak hours

  • Monday to Friday – 8:00 to 10:00, 16:30 to 19:00
  • Saturday – 10:00 to 14:00, 18:00 to 23:00
  • Sunday – 18:00 to 23:00

Fare cap

To protect commuters from over charging, there are fare caps. There is a daily cap and weekly cap. The cap is applicable based on journey is in same line or a different line.

Here are the cap details

From Line To Line Daily cap Weekly cap
Green Green 8 55
Red Red 12 70
Green Red 15 90
Red Green 15 90

Assignment Solution

Please find the solution for the given problem below. There are mainly three files.

  1. metro_fare_calculator.py – The main Python script that implements the fare calculation system.
  2. journeys.csv – Sample CSV input file with example journeys.
  3. README.text – Instructions on how to run the script and details of the solution.

Keep all these three files in the zip and submit it.

Python Code

metro_fare_calculator.py

import csv
from datetime import datetime

# Fare details
FARE_RULES = {
    ("Green", "Green"): {"peak": 2, "non_peak": 1, "daily_cap": 8, "weekly_cap": 55},
    ("Red", "Red"): {"peak": 3, "non_peak": 2, "daily_cap": 12, "weekly_cap": 70},
    ("Green", "Red"): {"peak": 4, "non_peak": 3, "daily_cap": 15, "weekly_cap": 90},
    ("Red", "Green"): {"peak": 3, "non_peak": 2, "daily_cap": 15, "weekly_cap": 90},
}

# Peak hour time ranges
PEAK_HOURS = {
    "Monday": [(8, 0, 10, 0), (16, 30, 19, 0)],
    "Tuesday": [(8, 0, 10, 0), (16, 30, 19, 0)],
    "Wednesday": [(8, 0, 10, 0), (16, 30, 19, 0)],
    "Thursday": [(8, 0, 10, 0), (16, 30, 19, 0)],
    "Friday": [(8, 0, 10, 0), (16, 30, 19, 0)],
    "Saturday": [(10, 0, 14, 0), (18, 0, 23, 0)],
    "Sunday": [(18, 0, 23, 0)],
}

# Function to check if time is within peak hours
def is_peak_hour(journey_time):
    weekday = journey_time.strftime("%A")
    hour = journey_time.hour
    minute = journey_time.minute
    for time_range in PEAK_HOURS.get(weekday, []):
        start_hour, start_minute, end_hour, end_minute = time_range
        if (hour > start_hour or (hour == start_hour and minute >= start_minute)) and \
            (hour < end_hour or (hour == end_hour and minute <= end_minute)):
            return True
    return False

# Function to calculate fare for a single journey
def calculate_fare(from_line, to_line, journey_time):
    is_peak = is_peak_hour(journey_time)
    fare_type = "peak" if is_peak else "non_peak"
    fare_info = FARE_RULES.get((from_line, to_line))
    return fare_info[fare_type]

# Function to process the CSV and calculate the total fare
def calculate_total_fare(file_path):
    total_fare = 0
    daily_fares = {}
    weekly_fares = {}

    with open(file_path, 'r') as csvfile:
        csvreader = csv.reader(csvfile)
        for row in csvreader:
            from_line, to_line, journey_time_str = row
            from_line=from_line.strip()
            to_line=to_line.strip()
            journey_time_str=journey_time_str.strip()

            journey_time = datetime.fromisoformat(journey_time_str)

            # Calculate fare for the current journey
            fare = calculate_fare(from_line, to_line, journey_time)

            # Handle daily cap
            date_key = journey_time.date()
            fare_info = FARE_RULES.get((from_line, to_line))

            if date_key not in daily_fares:
                daily_fares[date_key] = {}
            if (from_line, to_line) not in daily_fares[date_key]:
                daily_fares[date_key][(from_line, to_line)] = 0

            daily_fares[date_key][(from_line, to_line)] += fare
            if daily_fares[date_key][(from_line, to_line)] > fare_info["daily_cap"]:
                daily_fares[date_key][(from_line, to_line)] = fare_info["daily_cap"]

            # Handle weekly cap
            week_key = journey_time.isocalendar()[1]  # Get the week number
            if week_key not in weekly_fares:
                weekly_fares[week_key] = {}
            if (from_line, to_line) not in weekly_fares[week_key]:
                weekly_fares[week_key][(from_line, to_line)] = 0

            weekly_fares[week_key][(from_line, to_line)] += fare
            if weekly_fares[week_key][(from_line, to_line)] > fare_info["weekly_cap"]:
                weekly_fares[week_key][(from_line, to_line)] = fare_info["weekly_cap"]

            total_fare += fare

    return total_fare

if __name__ == "__main__":
    file_path = 'journeys.csv'
    total_fare = calculate_total_fare(file_path)
    print(f"Total fare for the journey: ${total_fare}")

journeys.csv

Green, Green, 2021-03-24T07:58:30
Green, Red, 2021-03-24T09:58:30
Red, Red, 2021-03-25T11:58:30

README.text

Development Steps and Design

Step 1: Parsing Input

The input is given in CSV format, so we’ll start by reading the CSV data containing the journey details (FromLine, ToLine, DateTime).

Step 2: Peak Hours Check

We’ll define the peak hours for each day of the week, then check if the journey falls within the peak hours or non-peak hours.

Step 3: Fare Calculation

Based on the journey’s FromLine, ToLine, and whether it’s during peak or non-peak hours, we’ll calculate the fare for that journey.

Step 4: Fare Capping

To prevent overcharging, we’ll implement daily and weekly fare caps. We need to track the total fare for each day and week and apply the fare caps accordingly.

Step 5: Output the Total Fare

After processing all journeys, the final output will be the total fare applied.


If you have any doubts or need clarification, comment below. All the best!

Leave a Reply

Your email address will not be published. Required fields are marked *