feat(curriculum): add media catalogue workshop (#62051)

Co-authored-by: Ilenia <26656284+ilenia-magoni@users.noreply.github.com>
Co-authored-by: Zaira <33151350+zairahira@users.noreply.github.com>
This commit is contained in:
Dario
2025-09-18 08:54:23 +02:00
committed by GitHub
parent 7e81540d2e
commit a6d186a73d
46 changed files with 4872 additions and 3 deletions
+5 -3
View File
@@ -4484,9 +4484,11 @@
"Learn about Understanding Inheritance and Polymorphism in these lectures."
]
},
"workshop-placeholder-oop-2": {
"title": "Placeholder - Waiting for title",
"intro": [""]
"workshop-media-catalogue": {
"title": "Build a Media Catalogue",
"intro": [
"In this workshop, you will create a media catalogue application using object-oriented programming principles."
]
},
"lab-polygon-area-calculator": {
"title": "Build a Polygon Area Calculator",
@@ -0,0 +1,9 @@
---
title: Introduction to the Build a Media Catalogue
block: workshop-media-catalogue
superBlock: full-stack-developer
---
## Introduction to the Build Media Catalogue
In this workshop, you will create a media catalogue application using object-oriented programming principles.
@@ -0,0 +1,80 @@
---
id: 68af272f60e009597dd861e1
title: Step 1
challengeType: 20
dashedName: step-1
---
# --description--
In this workshop, you are going to create a media catalogue. Start by defining a class named `Movie`.
Give your class an `__init__` method with the following parameters: `self`, `title`, `year`, `director`, and `duration`. Inside the `__init__` method, assign `title`, `year`, `director`, and `duration` parameters to instance attributes with the same name.
# --hints--
You should create a class named `Movie`.
```js
({
test: () => runPython(`assert _Node(_code).has_class("Movie")`)
})
```
Your `Movie` class should have an `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("Movie").has_function("__init__")`)
})
```
Your `__init__` method should have the following parameters: `self`, `title`, `year`, `director`, and `duration`.
```js
({
test: () => runPython(`assert _Node(_code).find_class("Movie").find_function("__init__").has_args("self, title, year, director, duration")`)
})
```
You should assign `title` to `self.title` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("Movie").find_function("__init__").has_stmt("self.title = title")`)
})
```
You should assign `year` to `self.year` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("Movie").find_function("__init__").has_stmt("self.year = year")`)
})
```
You should assign `director` to `self.director` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("Movie").find_function("__init__").has_stmt("self.director = director")`)
})
```
You should assign `duration` to `self.duration` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("Movie").find_function("__init__").has_stmt("self.duration = duration")`)
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,59 @@
---
id: 68af273960e009597dd861e2
title: Step 2
challengeType: 20
dashedName: step-2
---
# --description--
You want a readable display for debugging and printing. Create a `__str__` method that returns a string with the format `title (year) - duration min, director` (where `title`, `year`, `duration`, and `director` should be replaced with the values of the corresponding attributes) so that printing a movie gives a nice summary.
# --hints--
Your `Movie` class should have a `__str__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("Movie").has_function("__str__")`)
})
```
Your `__str__` method should return a string in the format `title (year) - duration min, director`, where `title`, `year`, `duration`, and `director` should be replaced with the values of the correspondant attributes.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
print(movie1)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
assert "The Matrix (1999) - 136 min, The Wachowskis" in output
`)
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class Movie:
def __init__(self, title, year, director, duration):
self.title = title
self.year = year
self.director = director
self.duration = duration
--fcc-editable-region--
```
@@ -0,0 +1,57 @@
---
id: 68af274560e009597dd861e3
title: Step 3
challengeType: 20
dashedName: step-3
---
# --description--
Now, create an instance of `Movie` passing in arguments of your choice and assign it to a variable named `movie1`. Then, print `movie1` to the console.
# --hints--
You should have a variable named `movie1`.
```js
({
test: () => runPython(`assert _Node(_code).has_variable("movie1")`)
})
```
You should assign an instance of `Movie` to the `movie1` variable.
```js
({
test: () => runPython(`
assert isinstance(movie1, Movie)
`)
})
```
You should print `movie1` to the console.
```js
({
test: () => runPython(`assert _Node(_code).has_call("print(movie1)")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,74 @@
---
id: 68af274e60e009597dd861e4
title: Step 4
challengeType: 20
dashedName: step-4
---
# --description--
In the previous steps, you created a basic movie object. Now, you'll add some validation to make sure that movies cannot be instantiated with invalid attributes.
At the beginning of the `__init__` method, create an `if` statement that checks if the `title` is either empty or contains whitespace only (you can use the `strip` method to build the condition).
If so, raise a `ValueError` with the message `Title cannot be empty`.
# --hints--
You should raise a `ValueError` with the message `Title cannot be empty` when `title` is either empty or contains whitespace only.
```js
({
test: () => runPython(`
_msg = 'Title cannot be empty'
_expected_error = f"Expected to raise 'ValueError: {_msg}'"
try:
Movie(' ', 1999, 'The Wachowskis', 136)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
try:
Movie(' ', 1999, 'The Wachowskis', 136)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
`)
})
```
You should not raise a `ValueError` when `title` is a valid string.
```js
({
test: () => runPython(`
try:
Movie('The Matrix', 1999, 'The Wachowskis', 136)
except ValueError:
assert False, 'Expected not to raise a ValueError with valid title'
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
--fcc-editable-region--
def __init__(self, title, year, director, duration):
self.title = title
self.year = year
self.director = director
self.duration = duration
--fcc-editable-region--
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
print(movie1)
```
@@ -0,0 +1,72 @@
---
id: 68af275860e009597dd861e5
title: Step 5
challengeType: 20
dashedName: step-5
---
# --description--
The first movie was made in 1895. Create another `if` statement that checks if `year` is less than `1895` and raise a `ValueError` with the message `Year must be 1895 or later`.
# --hints--
You should raise a `ValueError` with the message `Year must be 1895 or later` when `year` is less than `1895`.
```js
({
test: () => runPython(`
_msg = 'Year must be 1895 or later'
_expected_error = f"Expected to raise 'ValueError: {_msg}'"
try:
Movie('The Matrix', 1894, 'The Wachowskis', 136)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
try:
Movie('The Matrix', 0, 'The Wachowskis', 136)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
`)
})
```
You should not raise a `ValueError` when `year` is a valid number.
```js
({
test: () => runPython(`
try:
Movie('The Matrix', 1895, 'The Wachowskis', 136)
except ValueError:
assert False, 'Expected not to raise a ValueError with valid year'
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
--fcc-editable-region--
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
self.title = title
self.year = year
self.director = director
self.duration = duration
--fcc-editable-region--
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
print(movie1)
```
@@ -0,0 +1,74 @@
---
id: 68af276360e009597dd861e6
title: Step 6
challengeType: 20
dashedName: step-6
---
# --description--
Create another `if` statement to check if `director` is either empty or contains whitespace only and raise a `ValueError` with the message `Director cannot be empty`.
# --hints--
You should raise a `ValueError` with the message `Director cannot be empty` when `director` is either empty or contains whitespace only.
```js
({
test: () => runPython(`
_msg = 'Director cannot be empty'
_expected_error = f"Expected to raise 'ValueError: {_msg}'"
try:
Movie('The Matrix', 1999, ' ', 136)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
try:
Movie('The Matrix', 1999, ' ', 136)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
`)
})
```
You should not raise a `ValueError` when `director` is a valid string.
```js
({
test: () => runPython(`
try:
Movie('The Matrix', 1999, 'The Wachowskis', 136)
except ValueError:
assert False, 'Expected not to raise a ValueError with valid director'
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
--fcc-editable-region--
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
self.title = title
self.year = year
self.director = director
self.duration = duration
--fcc-editable-region--
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
print(movie1)
```
@@ -0,0 +1,76 @@
---
id: 68af276c60e009597dd861e7
title: Step 7
challengeType: 20
dashedName: step-7
---
# --description--
Finally, create one last `if` statement to verify if `duration` is not greater than `0` and raise a `ValueError` with the message `Duration must be positive`.
# --hints--
You should raise a `ValueError` with the message `Duration must be positive` when `duration` is not a positive number.
```js
({
test: () => runPython(`
_msg = 'Duration must be positive'
_expected_error = f"Expected to raise 'ValueError: {_msg}'"
try:
Movie('The Matrix', 1999, 'The Wachowskis', 0)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
try:
Movie('The Matrix', 1999, 'The Wachowskis', -1)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
`)
})
```
You should not raise a `ValueError` when `duration` is a valid number.
```js
({
test: () => runPython(`
try:
Movie('The Matrix', 1999, 'The Wachowskis', 136)
except ValueError:
assert False, 'Expected not to raise a ValueError with valid duration'
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
--fcc-editable-region--
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
self.title = title
self.year = year
self.director = director
self.duration = duration
--fcc-editable-region--
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
print(movie1)
```
@@ -0,0 +1,74 @@
---
id: 68af277560e009597dd861e8
title: Step 8
challengeType: 20
dashedName: step-8
---
# --description--
Now delete your `print(movie1)` line. Then, put your `movie1` assignment in a `try` block and create an `except` clause that catches `ValueError as e` and uses an f-string to print `Validation Error:` followed by a space and the error message.
Feel free to change the arguments passed to instantiate `movie1` to see the different error messages.
# --hints--
You should not have `print(movie1)` in your code.
```js
({
test: () => runPython(`assert not _Node(_code).has_call("print(movie1)")`)
})
```
You should have a `try` block with `movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)` inside it.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].find_body().has_stmt("movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)")`)
})
```
You should have an `except` clause that catches `ValueError as e`.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].has_except("ValueError", "e")`)
})
```
You should use an f-string to print `Validation Error:` followed by a space and the error message within the `except` block.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].find_except("ValueError", "e").has_call("print(f'Validation Error: {e}')")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
print(movie1)
--fcc-editable-region--
```
@@ -0,0 +1,72 @@
---
id: 68af277e60e009597dd861e9
title: Step 9
challengeType: 20
dashedName: step-9
---
# --description--
Now create a new class named `MediaCatalogue`, and give it an `__init__` method that sets an `items` attribute to an empty list. This will represent the list of items in the catalogue.
# --hints--
You should have a class named `MediaCatalogue`.
```js
({
test: () => runPython(`assert _Node(_code).has_class("MediaCatalogue")`)
})
```
Your `MediaCatalogue` class should have an `__init__` method.
```js
({
test: () => runPython(`
assert _Node(_code).find_class("MediaCatalogue").has_function("__init__")
`)
})
```
Your `__init__` method should set the `items` attribute to an empty list .
```js
({
test: () => runPython(`
_catalogue = MediaCatalogue()
assert _catalogue.items == []
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
--fcc-editable-region--
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,77 @@
---
id: 68af278a60e009597dd861ea
title: Step 10
challengeType: 20
dashedName: step-10
---
# --description--
The `MediaCatalogue` class needs a method to add media items to the catalogue. Create an `add` method that accepts an item `media_item` and appends it to the `items` list.
# --hints--
Your `MediaCatalogue` should have an `add` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").has_function("add")`)
})
```
Your `add` method should have two parameters: `self` and `media_item`.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("add").has_args("self, media_item")`)
})
```
Your `add` method should append the item passed to it to the `items` attribute.
```js
({
test: () => runPython(`
_catalogue = MediaCatalogue()
_catalogue.add("first")
assert _catalogue.items == ["first"]
_catalogue.add("second")
assert _catalogue.items == ["first", "second"]
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
class MediaCatalogue:
def __init__(self):
self.items = []
--fcc-editable-region--
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,88 @@
---
id: 68b6cc518177b0a11c3fe3b1
title: Step 14
challengeType: 20
dashedName: step-14
---
# --description--
Before the `try` block, create an instance of `MediaCatalogue` and assign it to a variable named `catalogue`.
Then, use the `add` method to append `movie1` to the catalogue. Finally print `catalogue` to the console.
# --hints--
You should assign an instance of `MediaCatalogue` to a variable named `catalogue` before your `try` block.
```js
({
test: () => runPython(`assert _Node(_code).has_stmt("catalogue = MediaCatalogue()")`)
})
```
You should have `catalogue.add(movie1)` in your `try` block.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].find_body().has_stmt("catalogue.add(movie1)")`)
})
```
You should print `catalogue` at the end of your `try` block.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].find_body().is_ordered("catalogue.add(movie1)", "print(catalogue)")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
--fcc-editable-region--
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
--fcc-editable-region--
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,91 @@
---
id: 68b6d1f70fffb10287c8031e
title: Step 15
challengeType: 20
dashedName: step-15
---
# --description--
Create another variable named `movie2` and assign it a `Movie` object instantiated with arguments of your choice. Then append `movie2` to the catalogue.
# --hints--
You should have a variable named `movie2`.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].find_body().has_variable("movie2")`)
})
```
You should assign an instance of `Movie` to the `movie2` variable.
```js
({
test: () => runPython(`
assert isinstance(movie2, Movie)
`)
})
```
You should have `catalogue.add(movie2)` in your `try` block.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].find_body().has_stmt("catalogue.add(movie2)")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
--fcc-editable-region--
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
--fcc-editable-region--
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,92 @@
---
id: 68b6d5244e6ce639e52fd1e7
title: Step 16
challengeType: 20
dashedName: step-16
---
# --description--
What if you want to represent a different type of media item, let's say a tv series? You could create another class and make it inherit from `Movie` to have access to all of its methods and attributes.
Create an empty class named `TVSeries` and make it inherit from `Movie`. Here's how to make a `Child` class inherit from a `Parent` class.
```py
class Parent:
...
class Child(Parent):
...
```
# --hints--
You should have a class named `TVSeries`.
```js
({
test: () => runPython(`assert _Node(_code).has_class("TVSeries")`)
})
```
Your `TVSeries` class should inherit from `Movie`.
```js
({
test: () => runPython(`assert _Node(_code).find_class("TVSeries").inherits_from("Movie")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
--fcc-editable-region--
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,101 @@
---
id: 68b6d9692a6526838265ad7f
title: Step 17
challengeType: 20
dashedName: step-17
---
# --description--
A `TVSeries` object will be instantiated with additional attributes with respect to its parent `Movie`. Therefore, create an `__init__` method with the following parameters: `self`, `title`, `year`, `director`, `duration`, `seasons`, and `total_episodes`.
Inside the `__init__` method, assign `seasons` and `total_episodes` to attributes with the same name.
# --hints--
Your `TVSeries` class should have an `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("TVSeries").has_function("__init__")`)
})
```
Your `__init__` method should have the following parameters: `self`, `title`, `year`, `director`, `duration`, `seasons`, and `total_episodes`.
```js
({
test: () => runPython(`assert _Node(_code).find_class("TVSeries").find_function("__init__").has_args("self, title, year, director, duration, seasons, total_episodes")`)
})
```
You should assign `seasons` to `self.seasons` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("TVSeries").find_function("__init__").has_stmt("self.seasons = seasons")`)
})
```
You should assign `total_episodes` to `self.total_episodes` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("TVSeries").find_function("__init__").has_stmt("self.total_episodes = total_episodes")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
class TVSeries(Movie):
pass
--fcc-editable-region--
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,106 @@
---
id: 68b702892910ab7b495de081
title: Step 11
challengeType: 20
dashedName: step-11
---
# --description--
You'll need a proper way to visually represent the catalogue. So create a `__str__` method in your `MediaCatalogue` class.
If the `items` list is empty, make sure the `__str__` method returns `Media Catalogue (empty)`.
# --hints--
You should have a `__str__` method in your `MediaCatalogue` class.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").has_function("__str__")`)
})
```
Your `__str__` method should return `Media Catalogue (empty)` when `self.items` is empty.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
print(MediaCatalogue())
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
assert "Media Catalogue (empty)" in output
`)
})
```
Your `__str__` method should not return `Media Catalogue (empty)` when `self.items` is not empty.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
_catalogue = MediaCatalogue()
_catalogue.add(Movie('The Matrix', 1999, 'The Wachowskis', 136))
try:
print(_catalogue)
except TypeError:
pass
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
assert "Media Catalogue (empty)" not in output
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
--fcc-editable-region--
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,71 @@
---
id: 68b704862dd6559e890c2a86
title: Step 12
challengeType: 20
dashedName: step-12
---
# --description--
After the `if` statement, create a variable named `result`. This will be the string to return from the method.
So assign it an f-string with the format `Media Catalogue (N items):`. Replace `N` with the number of items in the `items` list and make the string end with two newline characters.
# --hints--
You should have a variable named `result` in your `__str__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").has_variable("result")`)
})
```
Your should assign `f'Media Catalogue ({len(self.items)} items):\n\n'` to the `result` variable.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").has_stmt("result = f'Media Catalogue ({len(self.items)} items):\\\\n\\\\n'")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
--fcc-editable-region--
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
--fcc-editable-region--
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,159 @@
---
id: 68b7e1818bd294974b9099bf
title: Step 13
challengeType: 20
dashedName: step-13
---
# --description--
Right after declaring the `result` variable, create a `for` loop that uses enumeration to iterate over the items in the catalogue and appends to `result` a string with the following format: `N. movie` where `N` is a numeric index starting from `1` and `movie` is the string representation of the item.
Make the string end with a newline character and return `result` after the loop.
# --hints--
You should have a `for` loop in your `__str__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").find_for_loops()[0]`)
})
```
Your `for` loop should use the `enumerate` function.
```js
({
test: () => runPython(`assert "enumerate(" in str(_Node(_code).find_class("MediaCatalogue").find_function("__str__").find_for_loops()[0].find_for_iter())`)
})
```
Your `__str__` method should return `result`.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").has_return("result")`)
})
```
When `self.items` contains `Movie('Dances with Wolves', 1990, 'Kevin Costner', 224)` and `Movie('Annie Hall', 1977, 'Woody Allen', 93)`, your `__str__` method should return `Media Catalogue (2 items):\n\n1. Dances with Wolves (1990) - 224 min, Kevin Costner\n2. Annie Hall (1977) - 93 min, Woody Allen\n`.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
_catalogue = MediaCatalogue()
_catalogue.add(Movie('Dances with Wolves', 1990, 'Kevin Costner', 224))
_catalogue.add(Movie('Annie Hall', 1977, 'Woody Allen', 93))
print(_catalogue)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
_expected = """Media Catalogue (2 items):
1. Dances with Wolves (1990) - 224 min, Kevin Costner
2. Annie Hall (1977) - 93 min, Woody Allen
"""
assert _expected in output
`)
})
```
When `self.items` contains `Movie('Barry Lyndon', 1975, 'Stanley Kubrick', 184)` and `Movie('Fahrenheit 451', 1966, 'Francois Truffaut', 112)`, your `__str__` method should return `Media Catalogue (2 items):\n\n1. Barry Lyndon (1975) - 184 min, Stanley Kubrick\n2. Fahrenheit 451 (1966) - 112 min, Francois Truffaut\n`.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
_catalogue = MediaCatalogue()
_catalogue.add(Movie('Barry Lyndon', 1975, 'Stanley Kubrick', 184))
_catalogue.add(Movie('Fahrenheit 451', 1966, 'Francois Truffaut', 112))
print(_catalogue)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
_expected = """Media Catalogue (2 items):
1. Barry Lyndon (1975) - 184 min, Stanley Kubrick
2. Fahrenheit 451 (1966) - 112 min, Francois Truffaut
"""
assert _expected in output
`)
})
```
Your `__str__` method should return `Media Catalogue (empty)` when `self.items` is empty.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
print(MediaCatalogue())
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
assert "Media Catalogue (empty)" in output
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
--fcc-editable-region--
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
--fcc-editable-region--
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,82 @@
---
id: 68b804f3be853a355f055fef
title: Step 18
challengeType: 20
dashedName: step-18
---
# --description--
The `super` function is used to refer to the parent class without naming it explicitly. For example, `super().__init__(arg1, arg2, ...)` calls the `__init__` method defined in the parent class.
At the moment, you have effectively overridden the `__init__` method inherited from `Movie`. This means that a `TVSeries` object will have only a `seasons` attribute and a `total_episodes` attribute. This is not what you want.
To keep all the code defined in the `Movie` class' `__init__`, use `super` to call the parent's `__init__`. Note that you'll need to pass in the arguments required by the method. In this case, `title`, `year`, `director`, and `duration`.
# --hints--
You should call `super().__init__(title, year, director, duration)` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("TVSeries").find_function("__init__").has_stmt("super().__init__(title, year, director, duration)")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
class TVSeries(Movie):
def __init__(self, title, year, director, duration, seasons, total_episodes):
self.seasons = seasons
self.total_episodes = total_episodes
--fcc-editable-region--
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,149 @@
---
id: 68b80a995417adac579f9545
title: Step 19
challengeType: 20
dashedName: step-19
---
# --description--
Now, complete your `__init__` method by validating `seasons` and `total_episodes`. Create an `if` statement that checks if `seasons` is less than `1` and raises a `ValueError` with the message `Seasons must be 1 or greater`.
Then, do the same to validate `total_episodes`. This time use the message `Total episodes must be 1 or greater`.
# --hints--
You should raise a `ValueError` with the message `Seasons must be 1 or greater` when `seasons` is less than `1`.
```js
({
test: () => runPython(`
_msg = 'Seasons must be 1 or greater'
_expected_error = f"Expected to raise 'ValueError: {_msg}'"
try:
TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 0, 182)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
try:
TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, -1, 182)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
`)
})
```
You should not raise a `ValueError` when `seasons` is a valid number.
```js
({
test: () => runPython(`
try:
TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 1, 182)
except ValueError:
assert False, 'Expected not to raise a ValueError with valid seasons'
`)
})
```
You should raise a `ValueError` with the message `Total episodes must be 1 or greater` when `total_episodes` is less than `1`.
```js
({
test: () => runPython(`
_msg = 'Total episodes must be 1 or greater'
_expected_error = f"Expected to raise 'ValueError: {_msg}'"
try:
TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 0)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
try:
TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, -1)
except ValueError as e:
assert str(e) == _msg
else:
assert False, _expected_error
`)
})
```
You should not raise a `ValueError` when `total_episodes` is a valid number.
```js
({
test: () => runPython(`
try:
TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 1)
except ValueError:
assert False, 'Expected not to raise a ValueError with valid total_episodes'
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
--fcc-editable-region--
self.seasons = seasons
self.total_episodes = total_episodes
--fcc-editable-region--
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,101 @@
---
id: 68b80d521177c1e5469d31bf
title: Step 20
challengeType: 20
dashedName: step-20
---
# --description--
Now that the `__init__` method is complete, create a variable named `series1` inside the `try` block. Assign it an instance of `TVSeries` passing in arguments of your choice. Then, print `series1` to the console.
# --hints--
You should assign an instance of `TVSeries` to a variable named `series1` within the `try` block.
```js
({
test: () => runPython(`
assert _Node(_code).find_trys()[0].find_body().has_variable("series1")
assert isinstance(series1, TVSeries)
`)
})
```
You should print `series1` within the `try` block.
```js
({
test: () => runPython(`
assert _Node(_code).find_trys()[0].find_body().has_stmt("print(series1)")
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
--fcc-editable-region--
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
--fcc-editable-region--
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,94 @@
---
id: 68b80ef5aae8b2078b0a9dc1
title: Step 21
challengeType: 20
dashedName: step-21
---
# --description--
As you can see, `series1` is displayed in the same format of a `Movie` object because it's inheriting its parent's `__str__` method. In the next step, you'll override the `__str__` method to provide a custom string representation.
For now, delete the `print(series1)` line from your code.
# --hints--
You should not have `print(series1)` in your code
```js
({
test: () => runPython(`
assert not _Node(_code).find_trys()[0].find_body().has_stmt("print(series1)")
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
--fcc-editable-region--
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
print(series1)
--fcc-editable-region--
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,112 @@
---
id: 68b810b125078b2c943ce4e5
title: Step 22
challengeType: 20
dashedName: step-22
---
# --description--
Within the `TVSeries` class, create a `__str__` method that returns a string with the following format: `title (year) - S seasons, N episodes, duration min avg, director`, where `title`, `year`, `S`, `N`, `duration`, and `director` should be replaced by `title`, `year`, `seasons`, `total_episodes`, `duration` and `director` attributes, respectively.
# --hints--
Your `TVSeries` class should have a `__str__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("TVSeries").has_function("__str__")`)
})
```
Your `__str__` method should return a string in the format `title (year) - S seasons, N episodes, duration min avg, director`.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
print(series1)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
assert "Scrubs (2001) - 9 seasons, 182 episodes, 24 min avg, Bill Lawrence" in output
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
--fcc-editable-region--
--fcc-editable-region--
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,116 @@
---
id: 68b813ecdbe8896fd1fcf736
title: Step 23
challengeType: 20
dashedName: step-23
---
# --description--
Use the `add` method of `catalogue` to append `series1` to the `items` list.
Then, create another variable `series2`. Assign another instance of `TVSeries` to it and append `series2` to the catalogue as well.
# --hints--
You should use the `add` method of `catalogue` passing in `series1`.
```js
({
test: () => runPython(`
assert _Node(_code).find_trys()[0].find_body().has_stmt("catalogue.add(series1)")
`)
})
```
You should create a variable named `series2` and assign it an instance of `TVSeries`.
```js
({
test: () => runPython(`
assert _Node(_code).find_trys()[0].find_body().has_variable("series2")
assert isinstance(series2, TVSeries)
`)
})
```
You should use the `add` method of `catalogue` passing in `series2`.
```js
({
test: () => runPython(`
assert _Node(_code).find_trys()[0].find_body().has_stmt("catalogue.add(series2)")
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
--fcc-editable-region--
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
--fcc-editable-region--
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,107 @@
---
id: 68b816815f13eca63a588b45
title: Step 24
challengeType: 20
dashedName: step-24
---
# --description--
A docstring is a string placed as the first statement in a class or function, and it's used to provide documentation for that class or function. Docstrings are typically enclosed by triple double quotes:
```py
def add(x):
"""A function that returns the square of a number."""
return x + y
```
Complete your `Movie` class by adding the following docstring to it: `Parent class representing a movie.`.
# --hints--
You should add the docstring `Parent class representing a movie.` to your `Movie` class.
```js
({
test: () => runPython(`
_movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
assert _movie1.__doc__ == 'Parent class representing a movie.'
`)
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class Movie:
--fcc-editable-region--
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,103 @@
---
id: 68b942c9b8cce592f1c33d03
title: Step 26
challengeType: 20
dashedName: step-26
---
# --description--
Now complete the `TVSeries` class by adding the docstring `Child class representing an entire TV series.`.
# --hints--
You should add the docstring `Child class representing an entire TV series.` to your `TVSeries` class.
```js
({
test: () => runPython(`
_series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
assert _series1.__doc__ == 'Child class representing an entire TV series.'
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
--fcc-editable-region--
class TVSeries(Movie):
--fcc-editable-region--
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
print(movie1.__doc__)
print(series1.__doc__)
```
@@ -0,0 +1,109 @@
---
id: 68b94426d4f9aaaa9ed47203
title: Step 27
challengeType: 20
dashedName: step-27
---
# --description--
Now remove the two `print` calls from your code.
# --hints--
You should delete the `print(movie1.__doc__)` line.
```js
({
test: () => runPython(`assert not _Node(_code).has_call("print(movie1.__doc__)")`)
})
```
You should delete the `print(series1.__doc__)` line.
```js
({
test: () => runPython(`assert not _Node(_code).has_call("print(series1.__doc__)")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
--fcc-editable-region--
print(movie1.__doc__)
print(series1.__doc__)
--fcc-editable-region--
```
@@ -0,0 +1,108 @@
---
id: 68b94625a68b55cd53ec1f74
title: Step 25
challengeType: 20
dashedName: step-25
---
# --description--
Docstrings can be accessed through the `__doc__` attribute, which is set to `None` by default.
Verify it by printing `movie1.__doc__` and `series1.__doc__`. Note that the `__doc__` attribute is not inherited by any child class.
# --hints--
You should print `movie1.__doc__`.
```js
({
test: () => runPython(`assert _Node(_code).has_call("print(movie1.__doc__)")`)
})
```
You should print `series1.__doc__`.
```js
({
test: () => runPython(`assert _Node(_code).has_call("print(series1.__doc__)")`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,101 @@
---
id: 68b94a1a016e631239d7d10d
title: Step 28
challengeType: 20
dashedName: step-28
---
# --description--
Going back to the `MediaCatalogue` class, add a docstring to it saying `A catalogue that can store different types of media items.`
# --hints--
You should add the docstring `A catalogue that can store different types of media items.` to your `MediaCatalogue` class.
```js
({
test: () => runPython(`
assert MediaCatalogue().__doc__ == 'A catalogue that can store different types of media items.'
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
--fcc-editable-region--
class MediaCatalogue:
--fcc-editable-region--
def __init__(self):
self.items = []
def add(self, media_item):
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,140 @@
---
id: 68b94e26d2216958479e24bf
title: Step 29
challengeType: 20
dashedName: step-29
---
# --description--
It would be nice to validate the item passed to the `add` method before adding it to the catalogue.
Create an `if` statement that uses `isinstance` to check if `media_item` is an instance of `Movie` and raise a `TypeError` with the message `Only Movie or TVSeries instances can be added`.
Note that `isinstance(obj, ClassName)` returns `True` if `obj` is an instance of `ClassName` or *any of its subclasses*, because it considers the full inheritance chain.
# --hints--
You should raise a `TypeError` with the message `Only Movie or TVSeries instances can be added` when `media_item` is not an instance of `Movie` or its subclasses.
```js
({
test: () => runPython(`
_msg = 'Only Movie or TVSeries instances can be added'
_expected_error = f"Expected to raise 'TypeError: {_msg}'"
_c = MediaCatalogue()
try:
_c.add("Not a movie")
except TypeError as e:
assert str(e) == _msg
else:
assert False, _expected_error
try:
_c.add({})
except TypeError as e:
assert str(e) == _msg
else:
assert False, _expected_error
`)
})
```
You should not raise a `TypeError` when `media_item` is a valid item.
```js
({
test: () => runPython(`
_c = MediaCatalogue()
try:
_c.add(Movie('The Matrix', 1999, 'The Wachowskis', 136))
except TypeError:
assert False, 'Expected not to raise a TypeError with valid media_item'
try:
_c.add(TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182))
except TypeError:
assert False, 'Expected not to raise a TypeError with valid media_item'
`)
})
```
# --seed--
## --seed-contents--
```py
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
--fcc-editable-region--
def add(self, media_item):
self.items.append(media_item)
--fcc-editable-region--
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,129 @@
---
id: 68b95956da014b1a8e32ffb3
title: Step 30
challengeType: 20
dashedName: step-30
---
# --description--
In a previous module, you learned that you can build your own custom exceptions by inheriting from the `Exception` built-in class:
```py
class CustomError(Exception):
...
```
Create a class named `MediaError`. Make it inherit from the `Exception` class and add to it a docstring saying `Custom exception for media-related errors.`.
# --hints--
You should create a class named `MediaError`.
```js
({
test: () => runPython(`assert _Node(_code).has_class("MediaError")`)
})
```
Your `MediaError` class should inherit from `Exception`.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaError").inherits_from("Exception")`)
})
```
You should add the docstring `Custom exception for media-related errors.` to your `MediaError` class.
```js
({
test: () => runPython(`
assert MediaError.__doc__ == 'Custom exception for media-related errors.'
`)
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
--fcc-editable-region--
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise TypeError('Only Movie or TVSeries instances can be added')
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,127 @@
---
id: 68b95c952d50ed52abe02ed3
title: Step 31
challengeType: 20
dashedName: step-31
---
# --description--
Give your `MediaError` class an `__init__` method with the following parameters: `self`, `message`, and `obj`.
Inside it, call `super().__init__(message)` to ensure the mechanisms for storing and displaying the error message, which is already present in the `Exception` class, can work properly.
After that, assign `obj` to `self.obj`.
# --hints--
Your `MediaError` class should have an `__init__` method with `self`, `message`, and `obj` parameters.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaError").find_function("__init__").has_args("self, message, obj")`)
})
```
You should call `super().__init__(message)` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaError").find_function("__init__").has_stmt("super().__init__(message)")`)
})
```
You should assign `obj` to `self.obj` inside your `__init__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaError").find_function("__init__").has_stmt("self.obj = obj")`)
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
class MediaError(Exception):
"""Custom exception for media-related errors."""
--fcc-editable-region--
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise TypeError('Only Movie or TVSeries instances can be added')
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,195 @@
---
id: 68b95fa5f6e575879105e74f
title: Step 32
challengeType: 20
dashedName: step-32
---
# --description--
Now that the `MediaError` class is complete, modify the `if` statement in the `add` method. Replace the `TypeError` with a `MediaError` using the same message.
Also, pass in `media_item` as the second argument after the message. This will allow you to store the item causing the error in the exception object.
# --hints--
You should raise a `MediaError` with the message `Only Movie or TVSeries instances can be added` when `media_item` is not an instance of `Movie` or its subclasses.
```js
({
test: () => runPython(`
_msg = 'Only Movie or TVSeries instances can be added'
_expected_error = f"Expected to raise 'MediaError: {_msg}'"
_c = MediaCatalogue()
try:
_c.add("Not a movie")
except MediaError as e:
assert str(e) == _msg
else:
assert False, _expected_error
try:
_c.add({})
except MediaError as e:
assert str(e) == _msg
else:
assert False, _expected_error
`)
})
```
You should pass in `media_item` as the second argument to `MediaError`.
```js
({
test: () => runPython(`
_c = MediaCatalogue()
_obj = "Not a movie"
try:
_c.add(_obj)
except MediaError as e:
assert e.obj == _obj
else:
assert False
`)
})
```
You should not raise a `MediaError` when `media_item` is a valid item.
```js
({
test: () => runPython(`
_c = MediaCatalogue()
try:
_c.add(Movie('The Matrix', 1999, 'The Wachowskis', 136))
except MediaError:
assert False, 'Expected not to raise a MediaeError with valid media_item'
except TypeError:
pass
try:
_c.add(TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182))
except MediaError:
assert False, 'Expected not to raise a MediaError with valid media_item'
except TypeError:
pass
`)
})
```
Your `add` method should not raise a `TypeError`.
```js
({
test: () => runPython(`
_c = MediaCatalogue()
try:
_c.add(Movie('The Matrix', 1999, 'The Wachowskis', 136))
except TypeError:
assert False, 'Expected not to raise a TypeError from add method'
try:
_c.add(TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182))
except TypeError:
assert False, 'Expected not to raise a TypeError from add method'
try:
_c.add("Not a movie")
except TypeError:
assert False, 'Expected not to raise a TypeError from add method'
except MediaError:
pass
`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
--fcc-editable-region--
if not isinstance(media_item, Movie):
raise TypeError('Only Movie or TVSeries instances can be added')
--fcc-editable-region--
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
```
@@ -0,0 +1,119 @@
---
id: 68b96d7ba5421d758dbb8ab8
title: Step 33
challengeType: 20
dashedName: step-33
---
# --description--
Create another `except` clause that catches `MediaError as e`. Within the `except` block, use an f-string to print `Media Error:` followed by a space and the error message.
# --hints--
You should have an `except` clause that catches `MediaError as e`.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].has_except("MediaError", "e")`)
})
```
You should use an f-string to print `Media Error:` followed by a space and the error message within your new `except` block.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].find_except("MediaError", "e").has_call("print(f'Media Error: {e}')")`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
--fcc-editable-region--
except ValueError as e:
print(f'Validation Error: {e}')
--fcc-editable-region--
```
@@ -0,0 +1,113 @@
---
id: 68b96f1e9f14eb91ed0d9b98
title: Step 34
challengeType: 20
dashedName: step-34
---
# --description--
To see your custom exception in action, turn the line `catalogue.add(series2)` into `catalogue.add('series2')`.
# --hints--
You should turn the line `catalogue.add(series2)` into `catalogue.add('series2')`.
```js
({
test: () => runPython(`
assert _Node(_code).find_trys()[0].find_body().has_stmt("catalogue.add('series2')")
`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
--fcc-editable-region--
catalogue.add(series2)
--fcc-editable-region--
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
```
@@ -0,0 +1,118 @@
---
id: 68b970cf5f000fafa537dbbd
title: Step 35
challengeType: 20
dashedName: step-35
---
# --description--
Since you are storing the object causing the exception in the exception object, you can make some use of it.
After your existing `print` call, add `print(f'Unable to add {e.obj}: {type(e.obj)}')`.
This kind of messages can be extremely useful when you are trying to debug your code.
# --hints--
You should have `print(f'Unable to add {e.obj}: {type(e.obj)}')` after your existing `print(f'Media Error: {e}')` call.
```js
({
test: () => runPython(`assert _Node(_code).find_trys()[0].find_except("MediaError", "e").is_ordered("print(f'Media Error: {e}')", "print(f'Unable to add {e.obj}: {type(e.obj)}')")`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add('series2')
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
--fcc-editable-region--
except MediaError as e:
print(f'Media Error: {e}')
--fcc-editable-region--
```
@@ -0,0 +1,115 @@
---
id: 68b991f8b4aff7f09a06c0c2
title: Step 36
challengeType: 20
dashedName: step-36
---
# --description--
Remove the quotes around `series2` to restore the line you modified before.
# --hints--
You should have `catalogue.add(series2)` in your code.
```js
({
test: () => runPython(`
assert _Node(_code).find_trys()[0].find_body().has_stmt("catalogue.add(series2)")
`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
--fcc-editable-region--
catalogue.add('series2')
--fcc-editable-region--
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
print(f'Unable to add {e.obj}: {type(e.obj)}')
```
@@ -0,0 +1,139 @@
---
id: 68b99289c744bffa96807d04
title: Step 37
challengeType: 20
dashedName: step-37
---
# --description--
Currently, both movies and tv series are displayed all together when you print the catalogue. In the next steps, you'll take care of filtering the items in two categories to display separately.
Inside the `MediaCatalogue` class, create a method named `get_movies` and make it return a list containing only instances of the parent class `Movie` that can be found in `self.items`.
Use the approach you prefer to do so, but remember to use the `type` function since `isinstance` won't be able to discriminate between parent and child classes.
# --hints--
Your `MediaCatalogue` class should have a method named `get_movies`.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").has_function("get_movies")`)
})
```
Your `get_movies` method should return a list containg all the items in `self.items` that are instances of the `Movie` class.
```js
({
test: () => runPython(`
m1 = Movie("The Godfather", 1972, "Francis Ford Coppola", 175)
m2 = Movie("Pulp Fiction", 1994, "Quentin Tarantino", 154)
s1 = TVSeries("Game of Thrones", 2011, "David Benioff & D.B. Weiss", 57, 8, 73)
s2 = TVSeries("Stranger Things", 2016, "The Duffer Brothers", 50, 4, 34)
c = MediaCatalogue()
c.add(m1)
c.add(s1)
c.add(m2)
c.add(s2)
movies = c.get_movies()
assert len(movies) == 2
assert m1 in movies and m2 in movies
`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
--fcc-editable-region--
--fcc-editable-region--
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
print(f'Unable to add {e.obj}: {type(e.obj)}')
```
@@ -0,0 +1,138 @@
---
id: 68b995fb1712f635a9387fe8
title: Step 38
challengeType: 20
dashedName: step-38
---
# --description--
Create another method named `get_tv_series` and make it return a list with all the instances of `TVSeries` you can found in `self.items`.
# --hints--
Your `MediaCatalogue` class should have a method named `get_tv_series`.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").has_function("get_tv_series")`)
})
```
Your `get_tv_series` method should return a list containg all the items in `self.items` that are instances of the `TVSeries` class.
```js
({
test: () => runPython(`
m1 = Movie("The Godfather", 1972, "Francis Ford Coppola", 175)
m2 = Movie("Pulp Fiction", 1994, "Quentin Tarantino", 154)
s1 = TVSeries("Game of Thrones", 2011, "David Benioff & D.B. Weiss", 57, 8, 73)
s2 = TVSeries("Stranger Things", 2016, "The Duffer Brothers", 50, 4, 34)
c = MediaCatalogue()
c.add(m1)
c.add(s1)
c.add(m2)
c.add(s2)
series = c.get_tv_series()
assert len(series) == 2
assert s1 in series and s2 in series
`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def get_movies(self):
return [item for item in self.items if type(item) is Movie]
--fcc-editable-region--
--fcc-editable-region--
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
print(f'Unable to add {e.obj}: {type(e.obj)}')
```
@@ -0,0 +1,144 @@
---
id: 68b99699b8d2ab4092ba0623
title: Step 39
challengeType: 20
dashedName: step-39
---
# --description--
After the `if` statement in your `__str__` method, create two variables named `movies` and `series` and assign it a call to the `get_movies` method and a call to the `get_tv_series` method, respectively.
# --hints--
You should have a variable named `movies` in your `__str__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").has_variable("movies")`)
})
```
You assign `self.get_movies()` to your `movies` variable.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").has_stmt("movies = self.get_movies()")`)
})
```
You should have a variable named `series` in your `__str__` method.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").has_variable("series")`)
})
```
You assign `self.get_tv_series()` to your `series` variable.
```js
({
test: () => runPython(`assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").has_stmt("series = self.get_tv_series()")`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def get_movies(self):
return [item for item in self.items if type(item) is Movie]
def get_tv_series(self):
return [item for item in self.items if isinstance(item, TVSeries)]
--fcc-editable-region--
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
result = f'Media Catalogue ({len(self.items)} items):\n\n'
--fcc-editable-region--
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
print(f'Unable to add {e.obj}: {type(e.obj)}')
```
@@ -0,0 +1,137 @@
---
id: 68b9999b6cfccc74e268c0f1
title: Step 40
challengeType: 20
dashedName: step-40
---
# --description--
Before your `for` loop, create an `if` statement that adds the string `=== MOVIES ===` (ending with a newline character) to the current value of `result` when the `movies` list is not empty.
# --hints--
You should have an `if` statement in your `__str__` method that checks if the `movies` list is not empty.
```js
({
test: () => runPython(`
cond = _Node(_code).find_class("MediaCatalogue").find_function("__str__").find_ifs()[1].find_conditions()[0]
conds = ["movies", "len(movies)"]
assert any(cond.is_equivalent(c) for c in conds)
`)
})
```
You should have `result += '=== MOVIES ===\n'` inside your `if` statement.
```js
({
test: () => runPython(`
assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").find_ifs()[1].find_body().has_stmt("result += '=== MOVIES ===\\\\n'")
`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def get_movies(self):
return [item for item in self.items if type(item) is Movie]
def get_tv_series(self):
return [item for item in self.items if isinstance(item, TVSeries)]
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
movies = self.get_movies()
series = self.get_tv_series()
result = f'Media Catalogue ({len(self.items)} items):\n\n'
--fcc-editable-region--
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
--fcc-editable-region--
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
print(f'Unable to add {e.obj}: {type(e.obj)}')
```
@@ -0,0 +1,209 @@
---
id: 68b99aa2f4444c86af78c101
title: Step 41
challengeType: 20
dashedName: step-41
---
# --description--
Indent your existing `for` loop by one level so that it runs when the catalogue has movies in it. Then, replace `self.items` with `movies` to iterate over movies only.
# --hints--
You should indent the existing `for` loop by one level and replace `self.items` with `movies` to iterate over movies only.
```js
({
test: () => runPython(`
loop = """for i, movie in enumerate(movies, 1):
result += f'{i}. {movie}\\\\n'"""
assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").find_ifs()[1].find_body().is_ordered("result += '=== MOVIES ===\\\\n'", loop)
`)
})
```
When `self.items` contains `Movie('Dances with Wolves', 1990, 'Kevin Costner', 224)` and `Movie('Annie Hall', 1977, 'Woody Allen', 93)`, your `__str__` method should return `Media Catalogue (2 items):\n\n=== MOVIES ===\n1. Dances with Wolves (1990) - 224 min, Kevin Costner\n2. Annie Hall (1977) - 93 min, Woody Allen\n`.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
_catalogue = MediaCatalogue()
_catalogue.add(Movie('Dances with Wolves', 1990, 'Kevin Costner', 224))
_catalogue.add(Movie('Annie Hall', 1977, 'Woody Allen', 93))
print(_catalogue)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
_expected = """Media Catalogue (2 items):
=== MOVIES ===
1. Dances with Wolves (1990) - 224 min, Kevin Costner
2. Annie Hall (1977) - 93 min, Woody Allen
"""
assert _expected in output
`)
})
```
When `self.items` contains `Movie('Barry Lyndon', 1975, 'Stanley Kubrick', 184)` and `Movie('Fahrenheit 451', 1966, 'Francois Truffaut', 112)`, your `__str__` method should return `Media Catalogue (2 items):\n\n=== MOVIES ===\n1. Barry Lyndon (1975) - 184 min, Stanley Kubrick\n2. Fahrenheit 451 (1966) - 112 min, Francois Truffaut\n`.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
_catalogue = MediaCatalogue()
_catalogue.add(Movie('Barry Lyndon', 1975, 'Stanley Kubrick', 184))
_catalogue.add(Movie('Fahrenheit 451', 1966, 'Francois Truffaut', 112))
print(_catalogue)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
_expected = """Media Catalogue (2 items):
=== MOVIES ===
1. Barry Lyndon (1975) - 184 min, Stanley Kubrick
2. Fahrenheit 451 (1966) - 112 min, Francois Truffaut
"""
assert _expected in output
`)
})
```
Your `__str__` method should return `Media Catalogue (empty)` when `self.items` is empty.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
print(MediaCatalogue())
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
assert "Media Catalogue (empty)" in output
`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def get_movies(self):
return [item for item in self.items if type(item) is Movie]
def get_tv_series(self):
return [item for item in self.items if isinstance(item, TVSeries)]
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
movies = self.get_movies()
series = self.get_tv_series()
result = f'Media Catalogue ({len(self.items)} items):\n\n'
--fcc-editable-region--
if movies:
result += '=== MOVIES ===\n'
for i, movie in enumerate(self.items, 1):
result += f'{i}. {movie}\n'
--fcc-editable-region--
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
print(f'Unable to add {e.obj}: {type(e.obj)}')
```
@@ -0,0 +1,375 @@
---
id: 68b99be831fb229c9d950296
title: Step 42
challengeType: 20
dashedName: step-42
---
# --description--
Using the same logic, create an `if` statement that runs when the `series` list is not empty. Inside it, concatenate the string `=== TV SERIES ===` (ending with a newline character) to the current value of `result`.
Then, create a `for` loop that uses enumeration to add the items in the `series` list to the `result` variable. As you did previously, start indexing the values from `1`.
With that, the media catalogue workshop is complete.
# --hints--
You should have a second `if` statement in your `__str__` method.
```js
({
test: () => runPython(`
assert len(_Node(_code).find_class("MediaCatalogue").find_function("__str__").find_ifs()) == 3
`)
})
```
You should have `result += '=== TV SERIES ===\n'` inside your second `if` statement.
```js
({
test: () => runPython(`
assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").find_ifs()[2].find_body().has_stmt("result += '=== TV SERIES ===\\\\n'")
`)
})
```
You should have a `for` loop in your second `if` statement.
```js
({
test: () => runPython(`
assert _Node(_code).find_class("MediaCatalogue").find_function("__str__").find_ifs()[2].find_body().find_for_loops()[0]
`)
})
```
When `self.items` contains `Movie('Dances with Wolves', 1990, 'Kevin Costner', 224)` and `Movie('Annie Hall', 1977, 'Woody Allen', 93)`, your `__str__` method should return `Media Catalogue (2 items):\n\n=== MOVIES ===\n1. Dances with Wolves (1990) - 224 min, Kevin Costner\n2. Annie Hall (1977) - 93 min, Woody Allen\n`.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
_catalogue = MediaCatalogue()
_catalogue.add(Movie('Dances with Wolves', 1990, 'Kevin Costner', 224))
_catalogue.add(Movie('Annie Hall', 1977, 'Woody Allen', 93))
print(_catalogue)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
_expected = """Media Catalogue (2 items):
=== MOVIES ===
1. Dances with Wolves (1990) - 224 min, Kevin Costner
2. Annie Hall (1977) - 93 min, Woody Allen
"""
assert _expected in output
`)
})
```
When `self.items` contains `TVSeries("Game of Thrones", 2011, "David Benioff & D.B. Weiss", 57, 8, 73)` and `TVSeries("Stranger Things", 2016, "The Duffer Brothers", 50, 4, 34)`, your `__str__` method should return `Media Catalogue (2 items):\n\n=== TV SERIES ===\n1. Game of Thrones (2011) - 8 seasons, 73 episodes, 57 min avg, David Benioff & D.B. Weiss\n2. Stranger Things (2016) - 4 seasons, 34 episodes, 50 min avg, The Duffer Brothers\n`.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
_catalogue = MediaCatalogue()
_catalogue.add(TVSeries("Game of Thrones", 2011, "David Benioff & D.B. Weiss", 57, 8, 73))
_catalogue.add(TVSeries("Stranger Things", 2016, "The Duffer Brothers", 50, 4, 34))
print(_catalogue)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
_expected = """Media Catalogue (2 items):
=== TV SERIES ===
1. Game of Thrones (2011) - 8 seasons, 73 episodes, 57 min avg, David Benioff & D.B. Weiss
2. Stranger Things (2016) - 4 seasons, 34 episodes, 50 min avg, The Duffer Brothers
"""
assert _expected in output
`)
})
```
When `self.items` contains `Movie('Barry Lyndon', 1975, 'Stanley Kubrick', 184)`, `Movie('Fahrenheit 451', 1966, 'Francois Truffaut', 112)`, `TVSeries("Game of Thrones", 2011, "David Benioff & D.B. Weiss", 57, 8, 73)` and `TVSeries("Stranger Things", 2016, "The Duffer Brothers", 50, 4, 34)` your `__str__` method should return `Media Catalogue (4 items):\n\n=== MOVIES ===\n1. Barry Lyndon (1975) - 184 min, Stanley Kubrick\n2. Fahrenheit 451 (1966) - 112 min, Francois Truffaut\n=== TV SERIES ===\n1. Game of Thrones (2011) - 8 seasons, 73 episodes, 57 min avg, David Benioff & D.B. Weiss\n2. Stranger Things (2016) - 4 seasons, 34 episodes, 50 min avg, The Duffer Brothers\n`.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
_catalogue = MediaCatalogue()
_catalogue.add(Movie('Barry Lyndon', 1975, 'Stanley Kubrick', 184))
_catalogue.add(Movie('Fahrenheit 451', 1966, 'Francois Truffaut', 112))
_catalogue.add(TVSeries("Game of Thrones", 2011, "David Benioff & D.B. Weiss", 57, 8, 73))
_catalogue.add(TVSeries("Stranger Things", 2016, "The Duffer Brothers", 50, 4, 34))
print(_catalogue)
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
_expected = """Media Catalogue (4 items):
=== MOVIES ===
1. Barry Lyndon (1975) - 184 min, Stanley Kubrick
2. Fahrenheit 451 (1966) - 112 min, Francois Truffaut
=== TV SERIES ===
1. Game of Thrones (2011) - 8 seasons, 73 episodes, 57 min avg, David Benioff & D.B. Weiss
2. Stranger Things (2016) - 4 seasons, 34 episodes, 50 min avg, The Duffer Brothers
"""
assert _expected in output
`)
})
```
Your `__str__` method should return `Media Catalogue (empty)` when `self.items` is empty.
```js
({
test: () => runPython(`
import io
import sys
captured_output = io.StringIO()
sys.stdout = captured_output
print(MediaCatalogue())
sys.stdout = sys.__stdout__
output = captured_output.getvalue()
assert "Media Catalogue (empty)" in output
`)
})
```
# --seed--
## --seed-contents--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def get_movies(self):
return [item for item in self.items if type(item) is Movie]
def get_tv_series(self):
return [item for item in self.items if isinstance(item, TVSeries)]
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
movies = self.get_movies()
series = self.get_tv_series()
result = f'Media Catalogue ({len(self.items)} items):\n\n'
--fcc-editable-region--
if movies:
result += '=== MOVIES ===\n'
for i, movie in enumerate(movies, 1):
result += f'{i}. {movie}\n'
--fcc-editable-region--
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
print(f'Unable to add {e.obj}: {type(e.obj)}')
```
# --solutions--
```py
class MediaError(Exception):
"""Custom exception for media-related errors."""
def __init__(self, message, obj):
super().__init__(message)
self.obj = obj
class Movie:
"""Parent class representing a movie."""
def __init__(self, title, year, director, duration):
if not title.strip():
raise ValueError('Title cannot be empty')
if year < 1895:
raise ValueError('Year must be 1895 or later')
if not director.strip():
raise ValueError('Director cannot be empty')
if duration <= 0:
raise ValueError('Duration must be positive')
self.title = title
self.year = year
self.director = director
self.duration = duration
def __str__(self):
return f'{self.title} ({self.year}) - {self.duration} min, {self.director}'
class TVSeries(Movie):
"""Child class representing an entire TV series."""
def __init__(self, title, year, director, duration, seasons, total_episodes):
super().__init__(title, year, director, duration)
if seasons < 1:
raise ValueError('Seasons must be 1 or greater')
if total_episodes < 1:
raise ValueError('Total episodes must be 1 or greater')
self.seasons = seasons
self.total_episodes = total_episodes
def __str__(self):
return f'{self.title} ({self.year}) - {self.seasons} seasons, {self.total_episodes} episodes, {self.duration} min avg, {self.director}'
class MediaCatalogue:
"""A catalogue that can store different types of media items."""
def __init__(self):
self.items = []
def add(self, media_item):
if not isinstance(media_item, Movie):
raise MediaError('Only Movie or TVSeries instances can be added', media_item)
self.items.append(media_item)
def get_movies(self):
return [item for item in self.items if type(item) is Movie]
def get_tv_series(self):
return [item for item in self.items if isinstance(item, TVSeries)]
def __str__(self):
if not self.items:
return 'Media Catalogue (empty)'
movies = self.get_movies()
series = self.get_tv_series()
result = f'Media Catalogue ({len(self.items)} items):\n\n'
if movies:
result += '=== MOVIES ===\n'
for i, movie in enumerate(movies, 1):
result += f'{i}. {movie}\n'
if series:
result += '=== TV SERIES ===\n'
for i, s in enumerate(series, 1):
result += f'{i}. {s}\n'
return result
catalogue = MediaCatalogue()
try:
movie1 = Movie('The Matrix', 1999, 'The Wachowskis', 136)
catalogue.add(movie1)
movie2 = Movie('Inception', 2010, 'Christopher Nolan', 148)
catalogue.add(movie2)
series1 = TVSeries('Scrubs', 2001, 'Bill Lawrence', 24, 9, 182)
catalogue.add(series1)
series2 = TVSeries('Breaking Bad', 2008, 'Vince Gilligan', 47, 5, 62)
catalogue.add(series2)
print(catalogue)
except ValueError as e:
print(f'Validation Error: {e}')
except MediaError as e:
print(f'Media Error: {e}')
print(f'Unable to add {e.obj}: {type(e.obj)}')
```
@@ -0,0 +1,54 @@
{
"name": "Build a Media Catalogue",
"isUpcomingChange": true,
"dashedName": "workshop-media-catalogue",
"helpCategory": "Python",
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"blockLayout": "challenge-grid",
"blockType": "workshop",
"challengeOrder": [
{ "id": "68af272f60e009597dd861e1", "title": "Step 1" },
{ "id": "68af273960e009597dd861e2", "title": "Step 2" },
{ "id": "68af274560e009597dd861e3", "title": "Step 3" },
{ "id": "68af274e60e009597dd861e4", "title": "Step 4" },
{ "id": "68af275860e009597dd861e5", "title": "Step 5" },
{ "id": "68af276360e009597dd861e6", "title": "Step 6" },
{ "id": "68af276c60e009597dd861e7", "title": "Step 7" },
{ "id": "68af277560e009597dd861e8", "title": "Step 8" },
{ "id": "68af277e60e009597dd861e9", "title": "Step 9" },
{ "id": "68af278a60e009597dd861ea", "title": "Step 10" },
{ "id": "68b702892910ab7b495de081", "title": "Step 11" },
{ "id": "68b704862dd6559e890c2a86", "title": "Step 12" },
{ "id": "68b7e1818bd294974b9099bf", "title": "Step 13" },
{ "id": "68b6cc518177b0a11c3fe3b1", "title": "Step 14" },
{ "id": "68b6d1f70fffb10287c8031e", "title": "Step 15" },
{ "id": "68b6d5244e6ce639e52fd1e7", "title": "Step 16" },
{ "id": "68b6d9692a6526838265ad7f", "title": "Step 17" },
{ "id": "68b804f3be853a355f055fef", "title": "Step 18" },
{ "id": "68b80a995417adac579f9545", "title": "Step 19" },
{ "id": "68b80d521177c1e5469d31bf", "title": "Step 20" },
{ "id": "68b80ef5aae8b2078b0a9dc1", "title": "Step 21" },
{ "id": "68b810b125078b2c943ce4e5", "title": "Step 22" },
{ "id": "68b813ecdbe8896fd1fcf736", "title": "Step 23" },
{ "id": "68b816815f13eca63a588b45", "title": "Step 24" },
{ "id": "68b94625a68b55cd53ec1f74", "title": "Step 25" },
{ "id": "68b942c9b8cce592f1c33d03", "title": "Step 26" },
{ "id": "68b94426d4f9aaaa9ed47203", "title": "Step 27" },
{ "id": "68b94a1a016e631239d7d10d", "title": "Step 28" },
{ "id": "68b94e26d2216958479e24bf", "title": "Step 29" },
{ "id": "68b95956da014b1a8e32ffb3", "title": "Step 30" },
{ "id": "68b95c952d50ed52abe02ed3", "title": "Step 31" },
{ "id": "68b95fa5f6e575879105e74f", "title": "Step 32" },
{ "id": "68b96d7ba5421d758dbb8ab8", "title": "Step 33" },
{ "id": "68b96f1e9f14eb91ed0d9b98", "title": "Step 34" },
{ "id": "68b970cf5f000fafa537dbbd", "title": "Step 35" },
{ "id": "68b991f8b4aff7f09a06c0c2", "title": "Step 36" },
{ "id": "68b99289c744bffa96807d04", "title": "Step 37" },
{ "id": "68b995fb1712f635a9387fe8", "title": "Step 38" },
{ "id": "68b99699b8d2ab4092ba0623", "title": "Step 39" },
{ "id": "68b9999b6cfccc74e268c0f1", "title": "Step 40" },
{ "id": "68b99aa2f4444c86af78c101", "title": "Step 41" },
{ "id": "68b99be831fb229c9d950296", "title": "Step 42" }
]
}
@@ -705,6 +705,7 @@
"blocks": [
"lecture-understanding-object-oriented-programming-and-encapsulation",
"lecture-understanding-inheritance-and-polymorphism",
"workshop-media-catalogue",
"lab-polygon-area-calculator",
"lecture-understanding-abstraction",
"review-object-oriented-programming",