#### Assignment 1: The `Book` Class

Create a class called `Book`. The `__init__` method should take `title`, `author`, and `pages` as arguments. These should be stored as attributes.

Instantiate (create) two `Book` objects with the following details:
1.  Title: "The Hobbit", Author: "J.R.R. Tolkien", Pages: 310
2.  Title: "1984", Author: "George Orwell", Pages: 328

Print the `title` of the first book and the `author` of the second book.
class Book:
	def __init__(self, title, author, pages):
		self.title = title
		self.author = author
		self.pages = pages
		
	
		
book1 = Book("The Hobbit", "J.R.R. Tolkien", 310)
book2 = Book("1984", "George Orwell", 328)

print(f"The title of the first book is {book1.title}.")
print(f"The author of the second book is {book2.author}.")
#### Assignment 2: The `Book` Class with a Method

Modify the `Book` class from Assignment 1. Add a method called `get_summary()` that returns a string in the format: `"Title by Author, X pages"`.

Create a `Book` object for "The Hobbit" and call the `get_summary()` method on it. Print the returned string.
class Book:
	def __init__(self, title, author, pages):
		self.title = title
		self.author = author
		self.pages = pages
	
	def get_summary(self):
		print (f"Title: {self.title} by:{self.author}, pages: {self.pages}")
		
	
		
book1 = Book("The Hobbit", "J.R.R. Tolkien", 310)
book2 = Book("1984", "George Orwell", 328)

print(f"The title of the first book is {book1.title}.")
print(f"The author of the second book is {book2.author}.")
book1.get_summary()
#### Assignment 3: The `LightSwitch` Class

Model a simple light switch. Create a class called `LightSwitch`.
1.  In the `__init__` method, create a single attribute `is_on` and set its default value to `False`.
2.  Create a method `turn_on()` that sets `is_on` to `True` and prints "The light is now ON."
3.  Create a method `turn_off()` that sets `is_on` to `False` and prints "The light is now OFF."
4.  Create a method `get_status()` that prints "The light is ON" if `is_on` is `True`, and "The light is OFF" otherwise.

Instantiate a `LightSwitch` object, then test its methods.
class LightSwitch:
    def __init__(self):
        self.is_on = False

    def turn_on(self):
        self.is_on = True
        return("Light is on!")

    def turn_off(self):
        self.is_on = False
        return("Light is off!")

    def get_status(self):
    if self.is_on:
		    print("The Light is on!")
		else:
				print("The Light is off!")
        
 light1 = LightSwitch()
 
 light1.turn_on()
 light1.get_status()
#### Assignment 4: The `Product` Class

You are modeling a product for an e-commerce store. Create a class called `Product`.
1.  The `__init__` method should accept `name`, `price`, and `quantity_in_stock`.
2.  Create a method `calculate_total_value()` that returns the total value of the product in stock (price * quantity).
3.  Create a method `sell(amount)` that reduces the `quantity_in_stock` by the `amount` sold. Add a check to ensure you don't sell more products than are in stock. If the sale is successful, it should print a confirmation. If not, it should print an "Out of stock" message.

Create a `Product` object for "Laptop" with a price of `$1200` and a quantity of `10`. Test your methods.
# Your code goes here
class Product:
    def __init__(self, name, price, quantity):
        # TODO: Initialize attributes
        self.name = name
        self.price = price
        self.quantity_in_stock = quantity
        
    @property
    def quantity_in_stock(self):
        print(f'Quantity in stock: {self._quantity_in_stock}')
        return self._quantity_in_stock
	  
    @quantity_in_stock.setter
    def quantity_in_stock(self, value):
        if value < 0:
            raise ValueError("Quantity can't be negative")
        self._quantity_in_stock = value

    def calculate_total_value(self):
        # TODO: Implement calculation
        return self.price * self.quantity_in_stock

    def sell(self, amount):
        # TODO: Implement sell logic with a check
        if amount > self.quantity_in_stock:
            raise ValueError('Not enough in stonk')
        else:
            self.quantity_in_stock -= amount

# TODO: Create a product and test its methods

# Create a "Laptop" product (price $1200, quantity 10) and test your methods.
laptop = Product('Laptop',10,40)

laptop.sell(3)
laptop.quantity_in_stock
#### Assignment 5: The `Circle` Class

Create a class `Circle` that is initialized with a `radius`.
1.  The `__init__` method should store the `radius`.
2.  Create a method `calculate_area()` that returns the area of the circle (Area = π * r²). You can use `3.14159` for π.
3.  Create a method `calculate_circumference()` that returns the circumference of the circle (Circumference = 2 * π * r).

Create a `Circle` object with a radius of `5` and print its area and circumference.
class Circle:
	def __init__(self,radius):
			self.radius = radius
			self.pi = 3.14159
			
	def calculate_area(self):
			return self.pi * self.radius ** 2
		
	def calculate_circumference(self):
			return 2 * self.pi * self.radius
			
shapes = Circle(5)

print(f"Area:{shapes.calculate_area()}, Circumference: {shapes.calculate_circumference()}")
#### Assignment 6: The `Student` Class Revisited

Based on the `Student` class from the slides, add a new method.
1.  Start with the `Student` class that has `name`, `age`, and `grade` attributes.
2.  Add a method `set_grade(self, new_grade)` that allows you to update a student's grade. The new grade should be between 0 and 100. If the `new_grade` is valid, update the `self.grade` attribute. If it is invalid (e.g., less than 0 or greater than 100), print an error message and do not change the grade.

Create a `Student` object, print their initial grade, then use your new method to update it. Try updating it with both a valid and an invalid grade to test your logic.
class Student:
	def __init__(self, name, age, grade):
		self.name = name
		self.age = age
		self.grade = grade
		
	def set_grade(self, new_grade):
		if 0 <= new_grade <= 100:
			self.grade = new_grade
			print(f"Grade updated to {self.grade}.")
		else:
			print("Invalid grade. PLease try again.")
student = Student("Emma", 20, 85)
print(f"Initial grade:{student.grade}")
student.set_grade(95)
student.set_grade(-10)

s1 = Student("S1", 21, 90)
s2 = Student("S2", 29, 85)
s3 = Student("S3", 23, 70)
s4 = Student("S4", 28, 75)
s5 = Student("S5", 30, 92)

students = [s1, s2, s3, s4, s5]

sorted(students, key = lambda s: s.grade)
#### Assignment 7: Interacting Classes - `Pet` and `Owner`

This assignment models the relationship between a pet and its owner. This will require two classes.
1.  **`Pet` Class**:
    *   `__init__` should take `name` and `species` (e.g., "Dog", "Cat").
    *   Include a method `get_info()` that returns a string like `"Fido is a Dog"`.
2.  **`Owner` Class**:
    *   `__init__` should take an `owner_name`.
    *   It should also have an attribute `pets`, which should be initialized as an empty list `[]`.
    *   Include a method `add_pet(self, pet_object)` that adds a `Pet` object to the `pets` list.
    *   Include a method `show_pets()` that iterates through the `pets` list and prints the info for each pet using the pet's `get_info()` method.

Create an `Owner` object and two `Pet` objects. Add the pets to the owner and then call `show_pets()`.
class Pet:
	def __init__ (self, name,species):
		self.name=name
		self.species=species
	def get_info(self):
		return f"{self.name} is a {self.species}."
class Owner:
	def __init__ (self, owner_name):
		self.owner_name=owner_name
		self.pet=[]
	def add_pet(self, pet_object):
		self.pet.append(pet_object)
	def show_pets(self):
		print(f"{self.owner_name}'s pets:")
		for pet in self.pet:
			print(pet.get_info())
owner1=Owner('Leo')
pet1=Pet('Fido','Dog')
pet2=Pet('Lucky','Cat')
owner1.add_pet(pet1)
owner1.add_pet(pet2)
owner1.show_pets()
	
	
		

Here we want to modify the assignment 5so our class can also get radius in mm and cm as well.