Skip to content

Commit cf8959c

Browse files
Enhance laptop allocation logic with user input validation and sadness minimization
1 parent 84d03a0 commit cf8959c

1 file changed

Lines changed: 65 additions & 13 deletions

File tree

implement-laptop-allocation/laptop_allocaton.py

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@
44
from typing import List, Dict
55

66
class OperatingSystem(Enum):
7+
"""enumeration of available operating systems."""
78
MACOS = "macOS"
89
ARCH = "Arch Linux"
910
UBUNTU = "Ubuntu"
1011

1112
@dataclass(frozen=True)
1213
class Person:
14+
"""represents a person with a name, age, and their OS preferences."""
1315
name: str
1416
age: int
15-
# Sorted in order of preference, most preferred is first.
17+
# listed in order of preference
1618
preferred_operating_systems: List[OperatingSystem]
1719

1820

1921
@dataclass(frozen=True)
2022
class Laptop:
23+
"""represents a laptop with specifications and an operating system."""
2124
id: int
2225
manufacturer: str
2326
model: str
@@ -41,10 +44,7 @@ class Laptop:
4144
Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU),
4245
Laptop(id=4, manufacturer="Apple", model="MacBook", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS),
4346
Laptop(id=5, manufacturer="Apple", model="MacBook Air", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS),
44-
Laptop(id=6, manufacturer="Lenovo", model="ThinkPad", screen_size_in_inches=14, operating_system=OperatingSystem.ARCH),
45-
Laptop(id=7, manufacturer="Asus", model="ZenBook", screen_size_in_inches=13, operating_system=OperatingSystem.UBUNTU),
46-
Laptop(id=8, manufacturer="HP", model="Spectre", screen_size_in_inches=14, operating_system=OperatingSystem.MACOS),
47-
Laptop(id=9, manufacturer="Apple", model="MacBook Pro", screen_size_in_inches=16, operating_system=OperatingSystem.MACOS),
47+
Laptop(id=6, manufacturer="HP", model="Spectre", screen_size_in_inches=14, operating_system=OperatingSystem.MACOS),
4848
]
4949

5050
people: List[Person] = [
@@ -56,7 +56,11 @@ class Laptop:
5656
]
5757

5858
def user_prompt() -> Person:
59+
"""
60+
prompt the user to input their details and preferred operating systems
61+
"""
5962
try:
63+
6064
# strip() whitespace before processing (no need for str type here as input always returns a string)
6165
name = input("Please enter your first name: ").strip()
6266
if not name.isalpha():
@@ -76,15 +80,18 @@ def user_prompt() -> Person:
7680
preferred_os = input(f"Please enter your preferred operating systems in order of preference, separated by commas (e.g., {', '.join(valid_os)}): ").strip()
7781

7882
# split and validate the OS
79-
preferred_os_list = [os.strip() for os in preferred_os.split(",")]
83+
preferred_os_list = [os.strip() for os in preferred_os.split(",") if os.strip()]
84+
if not preferred_os_list:
85+
raise ValueError("You must enter at least one operating system.")
86+
8087
preferred_os_enum = []
8188
for os_name in preferred_os_list:
8289
if os_name not in valid_os:
8390
raise ValueError(f"Invalid operating system: {os_name}")
8491
# convert to enum
8592
preferred_os_enum.append(OperatingSystem(os_name))
8693

87-
return Person(name=name, age=age, preferred_operating_systems=preferred_os_enum)
94+
return Person(name=name, age=age, preferred_operating_systems=tuple(preferred_os_enum))
8895

8996
# throw an error and exit for invalid age and os input
9097
except ValueError as error:
@@ -93,14 +100,59 @@ def user_prompt() -> Person:
93100

94101

95102
def find_possible_laptops(available_laptops: List[Laptop], current_person: Person) -> List[Laptop]:
103+
"""
104+
find laptops that match a person's preferred operating systems.
105+
"""
96106
return [
97107
laptop for laptop in available_laptops
98108
if laptop.operating_system in current_person.preferred_operating_systems
99109
]
100110

101-
# allocated sequentially, in a first come first served order
102-
def allocate_laptops_sequentially(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]:
103-
"""
104-
Allocate laptops to people sequentially based on the order in the people list.
105-
This approach respects the 'wait your turn' principle.
106-
"""
111+
112+
def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]:
113+
"""
114+
allocate laptops to people to minimize total sadness.
115+
"""
116+
def calculate_sadness_score(person: Person, laptop: Laptop) -> int:
117+
try:
118+
return person.preferred_operating_systems.index(laptop.operating_system)
119+
except ValueError:
120+
return 100
121+
122+
allocated_laptops = {}
123+
124+
# create a shallow copy of the laptops list
125+
available_laptops = laptops[:]
126+
127+
for person in people:
128+
# ensure available_laptops is not empty before calling min
129+
if not available_laptops:
130+
raise ValueError("No laptops available to allocate.")
131+
132+
# use min() to select the laptop with the lowest sadness score for the person.
133+
# lambda function is scoring the person's preferences by calculating the "sadness score" for each laptop based on the index position
134+
best_laptop = min(available_laptops, key=lambda laptop: calculate_sadness_score(person, laptop), default=None)
135+
if best_laptop:
136+
allocated_laptops[person.name] = best_laptop
137+
available_laptops.remove(best_laptop)
138+
139+
140+
if len(allocated_laptops) != len(people):
141+
raise ValueError("Not enough laptops to allocate one to each person.")
142+
143+
return allocated_laptops
144+
145+
146+
def main():
147+
# Prompt the user for their details and add them to the people list
148+
new_person = user_prompt()
149+
people.append(new_person)
150+
151+
# Allocate laptops and print the results
152+
allocation = allocate_laptops(people, laptops_list)
153+
for name, laptop in allocation.items():
154+
print(f"{name} was allocated {laptop.manufacturer} {laptop.model} with {laptop.operating_system.value}")
155+
156+
# Ensure the script runs only when executed directly
157+
if __name__ == "__main__":
158+
main()

0 commit comments

Comments
 (0)