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.
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
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
Total fare applied
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
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 |
Please find the solution for the given problem below. There are mainly three files.
Keep all these three files in the zip and submit it.
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
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!