8
\$\begingroup\$

I solved this practice problem in preparation for school exams.

The owner of a taxi company wants a system that calculates how much money his taxis take in one day.

Write and test a program for the owner.

  • Your program must include appropriate prompts for the entry of data.

  • Error messages and other output need to be set out clearly and understandably.

  • All variables, constants and other identifiers must have meaningful names.

You will need to complete these three tasks. Each task must be fully tested.

TASK 1 – calculate the money owed for one trip

The cost of a trip in a taxi is calculated based on the numbers of KMs traveled and the type of taxi that you are traveling in. The taxi company has three different types of taxi available:

  • a saloon car, that seats up to 4,
  • a people carrier, that seats up 8,
  • a mini-van, that seats up to 12.

For a trip in a saloon car the base amount is RM2.50 and then a charge of RM1.00 per KM. For a trip in a people carrier the base amount is RM4.00 and a charge of RM1.25 per KM. For a trip in a mini- van the base amount is RM5.00 and a charge of RM1.50 per KM. The minimum trip length is 3KM and the maximum trip length is 50KM. Once the trip is complete a 6% service tax is added.

TASK 2 – record what happens in a day

The owner of the taxi company wants to keep a record of the journeys done by each one of his taxis in a day. For each one of his three taxis record the length of each trip and the number of people carried. Your program should store a maximum of 24 trips or 350km worth of trips, whichever comes first. Your program should be able to output a list of the jobs done by each one of the three taxis.

TASK 3 – calculate the money taken for all the taxis at the end of the day.

At the end of the day use the data stored for each taxi to calculate the total amount of money taken and the total number of people carried by each one of the three taxis. Using the average price of RM2.79 per litre use the information in the table below to calculate the fuel cost for each taxi:

  • the saloon car uses 7.4L/100km
  • the people carrier uses 8.6L/100km
  • and the mini-van uses 9.2L/100km

Provide a report for the owner to show this information.

Here is my rather messy solution:

saloon = []
people_car = []
mini = []
count = 0

total_seats = 0
total_cost = 0

mini_distance = 0
sln_distance = 0
ppl_distance = 0

def check(distance):
    try:
        distance = float(distance)
    except ValueError:
        print("Enter a number")
        return False

while True:
    print("[1] Add to List (" + repr(count) + " entered)")
    print("[2] View List")
    print("[3] View Information and Costs")
    print("[4] Exit")
    menu = input("Enter command: ")

    if menu == "1":
        clarify = input("Add to list? (yes/no) ")

        if clarify == "yes" and count < 25:
            while True:

                if count > 24:
                    print("You have reached the maximum amount of list inputs!")
                    break

                seats = input("How many riders? (type exit to exit) ")

                try:
                    seats = int(seats)
                except ValueError:
                    print("Please enter a number \n=======================================")
                    pass
                    break

                if int(seats) > 0 and int(seats) < 5:
                    car_type = input("You will be taking the saloon car? (yes/no) ")
                    if car_type == "yes":
                        distance = input("How far was the trip? ")
                        if check(distance) == False:
                            break

                        if distance > 0 and distance < 51 and sln_distance < 351:
                            count += 1
                            len_sum = float(2.50) + distance
                            total_sum = len_sum * 1.06
                            print("Price is: RM" + repr(round(total_sum, 2)) + "\n=======================================")
                            saloon.append("Riders: " + repr(seats) + "Revenue: RM" + repr(round(total_sum, 2)) + " | ")
                            total_seats += seats
                            total_cost += round(total_sum, 2)
                            sln_distance += distance

                        elif sln_distance > 350:
                            print("This car has reached it's limit")

                        elif distance < 1 or distance > 50:
                            print("Please enter a valid distance between 1 and 50")

                        else:
                            print("Have you tried digits?")

                    elif car_type == "no":
                        print("Command cancelled")

                    else:
                        print("Please redo")


                if int(seats) > 4 and int(seats) < 9:
                    car_type = input("You will be taking the people carrier? (yes/no)")
                    if car_type == "yes":
                        distance = input("How far was the trip? ")
                        if check(distance) == False:
                            break

                        if distance > 0 and distance < 51:
                            count += 1
                            dis_cos = distance * 1.25
                            len_sum = float(4) + dis_cos
                            total_sum = len_sum * 1.06
                            print("Price is: RM" + repr(round(total_sum, 2)) + "\n=======================================")
                            people_car.append("Riders: " + repr(seats) + "Revenue: RM" + repr(round(total_sum, 2)) + " | ")
                            total_seats += seats
                            total_cost += round(total_sum, 2)
                            ppl_distance += distance

                        elif ppl_distance > 350:
                            print("This car has reached it's limit")

                        elif distance < 1 or distance > 50:
                            print("Please enter a valid distance between 1 and 50")

                        else:
                            print("Have you tried digits?")

                    elif car_type == "no":
                        print("Please redo")

                    else:
                        print("Please redo")

                if int(seats) > 8 and int(seats) < 13:
                    car_type = input("You will be taking the mini van? (yes/no)")
                    if car_type == "yes":
                        distance = input("How far was the trip? ")
                        if check(distance) == False:
                            break

                        if distance > 0 and distance < 51:
                            count += 1
                            dis_cos = distance * 1.5
                            len_sum = float(5) + dis_cos
                            total_sum = len_sum * 1.06
                            print("Price is: RM" + repr(round(total_sum, 2)) + "\n=======================================")
                            mini.append("Riders: " + repr(seats) + "Revenue: RM" + repr(round(total_sum, 2)) + " | ")
                            total_seats += seats
                            total_cost += round(total_sum, 2)
                            mini_distance += distance

                        elif mini_distance > 350:
                            print("This car has reached it's limit")

                        elif distance < 1 or distance > 50:
                            print("Please enter a valid distance between 1 and 50")

                        else:
                            print("Have you tried digits?")

                    elif car_type == "no":
                        print("Please redo")

                    else:
                        print("Please enter a valid answer")

                elif seats == "exit":
                    break

                elif seats < 1 or seats > 12:
                    print("Enter within the given range")



                elif (not int(seats) > 0) or (not int(seats) < 13) or (not seats == "exit"):
                    print("========================================")


            if clarify == "no":
                break

            else:
                continue

        else:
            print("Please redo \n=======================================")

    elif count == int(24):
        count += 1
        print("You have reached the maximum amount of list inputs!")

    elif menu == "2":
        print("Saloon: " + ' '.join(saloon) + "\nPeople carrier: " + ' '.join(people_car) + "\nMini van: " + ' '.join(mini))
        print("========================================")

    elif menu == "3":

        print("Riders: " + repr(total_seats) + "\nCost: RM" + repr(total_cost))

        def calc(a):
            avg_cost = float(a) / 100
            return avg_cost * 7.4

        final_sln = float(calc(sln_distance))
        final_ppl = float(calc(ppl_distance))
        final_mini = float(calc(mini_distance))

        print("Saloon fuel cost: RM" + repr(round(final_sln, 2)))
        print("People carrier fuel cost: RM" + repr(round(final_ppl, 2)))
        print("Mini van fuel cost: RM" + repr(round(final_mini, 2)))
        print("========================================")

    elif menu == "4":
        prompt = input("Exit program? (yes/no)")
        if prompt == "yes":
            exit()
        else:
            print("Command canceled")
            continue
\$\endgroup\$
7
\$\begingroup\$

TASK 1

if distance > 0 and distance < 51 and sln_distance < 351:
    count += 1
    len_sum = float(2.50) + distance
    total_sum = len_sum * 1.06
  1. Move magic values out of the code, this means 2.50 and 1.06 should be stored somewhere other than where the calulation is.
  2. You duplicate this code, with a minor change when you check the people carrirer and minivan, and so it'd be better to make it a function.
  3. The distance has to be a minimum of 3.
  4. It'd be easier if you stored this function on a class.
class Vehicle:
    def __init__(self, base, travel):
        self.base = base
        self.travel = travel

    def cost(self, length):
        if not (3 <= length <= 50):
            raise ValueError('Invalid trip length')
        return (self.base + self.travel * length) * 1.06

TASK 2

count += 1
len_sum = float(2.50) + distance
total_sum = len_sum * 1.06
print("Price is: RM" + repr(round(total_sum, 2)) + "\n=======================================")
saloon.append("Riders: " + repr(seats) + "Revenue: RM" + repr(round(total_sum, 2)) + " | ")
total_seats += seats
total_cost += round(total_sum, 2)
sln_distance += distance
  1. You can perform all the calulations at the end. Just store the distance and amount of people carried in a list.
  2. Add this to your Vehicle class as a function.
  3. You have a maximum that you can record, but you don't take that into account when you're appending to your list.
class Vehicle:
    def __init__(self, ...):
        ...
        self.trips = []

    def record_trip(self, distance, people):
        if len(self.trips) + 1 > 24:
            raise ValueError("Can't store more than 24 trips")
        if sum(trip[0] for trip in self.trips) + distance > 350:
            raise ValueError("Can't store more than 350KM")
        self.trips.append((distance, people))

TASK 3

elif menu == "2":
    print("Saloon: " + ' '.join(saloon) + "\nPeople carrier: " + ' '.join(people_car) + "\nMini van: " + ' '.join(mini))
    print("========================================")

elif menu == "3":

    print("Riders: " + repr(total_seats) + "\nCost: RM" + repr(total_cost))

    def calc(a):
        avg_cost = float(a) / 100
        return avg_cost * 7.4

    final_sln = float(calc(sln_distance))
    final_ppl = float(calc(ppl_distance))
    final_mini = float(calc(mini_distance))

    print("Saloon fuel cost: RM" + repr(round(final_sln, 2)))
    print("People carrier fuel cost: RM" + repr(round(final_ppl, 2)))
    print("Mini van fuel cost: RM" + repr(round(final_mini, 2)))
    print("========================================")
  1. Only the 'sln' has a fuel efficency of 7.4L/100km, but you calculate them all against that value.
  2. You can add all this code into the Vehicle class.
class Vehicle:
    def __init__(self, ..., efficency):
        ...
        self.efficency = efficency

    def average_cost(self):
        return sum(trip[0] for trip in self.trips) * self.efficency

    def total_people(self):
        return sum(trip[1] for trip in self.trips)

  1. I'd change your questions so that they use the vechicle class.
  2. To simplify your code you could use cmd to perform all the actions you want.
import cmd

class Vehicle:
    def __init__(self, base, travel, efficency):
        self.base = base
        self.travel = travel
        self.trips = []
        self.efficency = efficency

    def cost(self, length):
        if not (3 <= length <= 50):
            raise ValueError('Invalid trip length')
        return (self.base + self.travel * length) * 1.06

    def distance(self):
        return sum(trip[0] for trip in self.trips)

    def record_trip(self, distance, people):
        if len(self.trips) + 1 > 24:
            raise ValueError("Can't store more than 24 trips")
        if self.distance() + distance > 350:
            raise ValueError("Can't store more than 350KM")
        self.trips.append((distance, people))

    def average_cost(self):
        return self.distance() * self.efficency

    def total_people(self):
        return sum(trip[1] for trip in self.trips)

    def gross_income(self):
        return sum(self.cost(distance) for distance, _ in self.trips)


def args(values, metas):
    values = values.split()
    if len(values) > len(metas):
        keys = ', '.join(m[0] for m in metas)
        raise ValueError(f'Too many keys should have {keys}')

    if len(values) < len(metas):
        keys = ', '.join(m[0] for m in metas[len(values):])
        raise ValueError(f'Missing {keys}')

    output = []
    for value, meta in zip(values, metas):
        try:
            value = meta[1](value)
        except ValueError as e:
            raise ValueError(f'Invalid value for {meta[0]}')
        output.append(value)
    return output


class VehicleRenter(cmd.Cmd):
    intro = 'Welcome to the taxi recorder.   Type help or ? to list commands.\n'
    prompt = '> '
    vehicles = {
        'saloon':         Vehicle(2.50, 1.00, 7.4/100),
        'people-carrier': Vehicle(4.00, 1.25, 8.6/100),
        'mini-van':       Vehicle(5.00, 1.50, 9.2/100),
    }

    def get_vehicle(self, vehicle):
        v = self.vehicles.get(vehicle, None)
        if v is None:
            keys = ', '.join(self.vehicles.keys())
            raise ValueError(f'Invalid vehicle {vehicle!r}, options are {keys}')
        return v

    def do_vehicles(self, _):
        """List all the vechicles available."""
        print(', '.join(self.vehicles.keys()))

    def do_cost(self, arg):
        """
        Calulate the cost of a trip.

        cost {vechicle name} {distance}
        cost saloon 3
        """
        try:
            vehicle, distance = args(arg, (('vehicle', str), ('distance', float)))
            print(self.get_vehicle(vehicle).cost(distance))
        except ValueError as e:
            print(e)

    def do_trip(self, arg):
        """
        Record a trip.

        trip {vechicle name} {distance} {people}
        trip saloon 3 1
        """
        try:
            vehicle, distance, people = args(arg, (('vehicle', str), ('distance', float), ('people', int)))
            self.get_vehicle(vehicle).record_trip(distance, people)
        except ValueError as e:
            print(e)


    def do_stats(self, _):
        """See the stats of the vehicles"""
        for name, v in self.vehicles.items():
            print(
                f'{name}:\n'
                f'  trips: {len(v.trips)}\n'
                f'  distance: {v.distance()}\n'
                f'  people: {v.total_people()}\n'
                f'  gross income: {v.gross_income():.2f}\n'
                f'  fuel: {v.average_cost():.2f}\n'
                f'  net profit: {v.gross_income() - v.average_cost():.2f}\n'
            )

if __name__ == '__main__':
    VehicleRenter().cmdloop()
Welcome to the taxi recorder.   Type help or ? to list commands.

> help

Documented commands (type help <topic>):
========================================
cost  help  stats  trip  vehicles

> help vehicles
List all the vechicles available.
> vehicles
saloon, people-carrier, mini-van
> cost saloon 3.1
5.936
> cost saloon 1
Invalid trip length
> trip saloon 3
Missing people
> trip saloon 3 1
> stats
saloon:
  trips: 1
  distance: 3
  people: 1
  gross income: 5.83
  fuel: 0.22
  net profit: 5.61

people-carrier:
  trips: 0
  distance: 0
  people: 0
  gross income: 0.00
  fuel: 0.00
  net profit: 0.00

mini-van:
  trips: 0
  distance: 0
  people: 0
  gross income: 0.00
  fuel: 0.00
  net profit: 0.00
\$\endgroup\$
  • \$\begingroup\$ Ahhh I see, the code looks amazing and I'll try it out later, thanks! \$\endgroup\$ – Skryptix Nov 3 '18 at 2:05
9
\$\begingroup\$

To start off, I suggest you split the chunk of if/elif statements into individual functions. By doing this, it'll be easier for someone reader (such as your teacher) to be able to tell what each part of the code is doing. It's also important because it'll help you remember your code when you come back to look at it.

The upper level if statements, which are checking for seats, the code underneath them can be split into individual functions. Doing this would greatly improve your code.

Good luck on your assignment!

\$\endgroup\$
  • \$\begingroup\$ Furthermore, functions would allow you to test the code, as stated in the requirements. \$\endgroup\$ – 200_success Nov 2 '18 at 4:55
  • \$\begingroup\$ Thanks for your advice! I'll test it out later, BTW thanks for helping me edit my post XD \$\endgroup\$ – Skryptix Nov 2 '18 at 5:03
0
\$\begingroup\$

The problem specs are ambiguous as to whether its 25 trips/350 km total, or per car. You should ask for clarification.

You can combine a lot of your numbers into a single data structure. I recommend one dataframe for the parameter data, and then another for the user-generated data.

import pandas as pd

taxi_specs = pd.DataFrame([[4,2.5,1,.0074],[9,4,1.25,.0086],[12,5,1.15,.0092]],
                          index=['saloon','people car','mini'],columns=['max passengers','base','per km','fuel usage'])
rides = pd.DataFrame(columns = ['passengers','distance','car type','price','fuel cost'])

def find_car_type(passengers):
    for index, row in taxi_specs.iterrows():
        if passengers <= row['max passengers']:
            return index

Now for each trip, you after you get number of passengers and distance, you can do:

car_type = find_car_type(passengers)
price = taxi_specs.loc[car_type]['base']+ taxi_specs.loc[car_type]['per km']*distance
fuel_costs = taxi_specs.loc[car_type]['fuel usage']*distance  
rides.append(pd.Series([passengers,distance,car_type,price,fuel_costs],
                       index=rides.columns),ignore_index=True)

You can get totals with totals = rides.groupby('car type')[['passengers','price','fuel cost']].sum(). You'll then have to do something along the lines of totals['price']=totals['price']*1.06, although if you to be more realistic, this is often rounded, and the rounding is done for each transaction, so you'll have to multiply by 1.06 first, then round, then take the total.

\$\endgroup\$
  • \$\begingroup\$ Would dataframe or class be better judging from the previous answer? Thanks a lot! \$\endgroup\$ – Skryptix Nov 3 '18 at 2:07

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.