diff --git a/client/i18n/locales/english/intro.json b/client/i18n/locales/english/intro.json index 8ae28c215be..a57118cb83b 100644 --- a/client/i18n/locales/english/intro.json +++ b/client/i18n/locales/english/intro.json @@ -4590,6 +4590,12 @@ "In this workshop, you will learn about classes, objects, and methods in Python by building a simple musical instrument inventory." ] }, + "workshop-email-simulator": { + "title": "Build an Email Simulator", + "intro": [ + "In this workshop you will implement classes and objects by building an email simulator that simulates sending, receiving, and managing emails between different users." + ] + }, "lab-budget-app": { "title": "Build a Budget App", "intro": [ diff --git a/client/src/pages/learn/full-stack-developer/workshop-email-simulator/index.md b/client/src/pages/learn/full-stack-developer/workshop-email-simulator/index.md new file mode 100644 index 00000000000..0525409e401 --- /dev/null +++ b/client/src/pages/learn/full-stack-developer/workshop-email-simulator/index.md @@ -0,0 +1,9 @@ +--- +title: Introduction to the Build an Email Simulator +block: workshop-email-simulator +superBlock: full-stack-developer +--- + +## Introduction to the Build an Email Simulator + +In this workshop you will implement classes and objects by building an email simulator that simulates sending, receiving, and managing emails between different users. diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6852ea0c3cadb54e897acdac.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6852ea0c3cadb54e897acdac.md new file mode 100644 index 00000000000..03c727bb3b3 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6852ea0c3cadb54e897acdac.md @@ -0,0 +1,36 @@ +--- +id: 6852ea0c3cadb54e897acdac +title: Step 1 +challengeType: 20 +dashedName: step-1 +--- + +# --description-- + +In this workshop, you are going to build an *Email Simulator* that simulates sending, receiving, and managing emails between different users. You'll learn about classes, objects, and how to organize code in an object-oriented way. + +Begin by creating a class named `Email` using the `class` keyword. + +# --hints-- + +You should create a class named `Email`. + +```js +({ + test: () => + { + assert(runPython(`(_Node(_code).has_class("Email"))`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6852ef95ccc27660275692cd.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6852ef95ccc27660275692cd.md new file mode 100644 index 00000000000..1309fc7a38e --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6852ef95ccc27660275692cd.md @@ -0,0 +1,49 @@ +--- +id: 6852ef95ccc27660275692cd +title: Step 2 +challengeType: 20 +dashedName: step-2 +--- + +# --description-- + +Your `Email` class needs to store information about each email when it's created. + +Inside your `Email` class, remove the existing `pass` keyword and create the `__init__` method that takes `self`, `sender`, `receiver`, `subject`, and `body` as parameters. + +# --hints-- + +You should define an `__init__` method inside the `Email` class. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").has_function("__init__")`)) + } + +}) +``` + +Your `__init__` method should have parameters: `self`, `sender`, `receiver`, `subject`, and `body`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").find_function("__init__").has_args("self, sender, receiver, subject, body")`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +--fcc-editable-region-- +class Email: + pass +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6852f01116912861c7d1b0e6.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6852f01116912861c7d1b0e6.md new file mode 100644 index 00000000000..0d873f45d8d --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6852f01116912861c7d1b0e6.md @@ -0,0 +1,74 @@ +--- +id: 6852f01116912861c7d1b0e6 +title: Step 3 +challengeType: 20 +dashedName: step-3 +--- + +# --description-- + +Inside the `__init__` method, you need to assign the parameter values to the object's attributes so each email can store its information. + +Inside your `__init__` method, remove the `pass` keyword and assign the parameters to `self.sender`, `self.receiver`, `self.subject`, and `self.body`. + +# --hints-- + +You should assign the `sender` parameter to `self.sender`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").find_function("__init__").find_variable("self.sender").is_equivalent("self.sender = sender")`)) + } + +}) +``` + +You should assign the `receiver` parameter to `self.receiver`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").find_function("__init__").find_variable("self.receiver").is_equivalent("self.receiver = receiver")`)) + } + +}) +``` + +You should assign the `subject` parameter to `self.subject`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").find_function("__init__").find_variable("self.subject").is_equivalent("self.subject = subject")`)) + } + +}) +``` + +You should assign the `body` parameter to `self.body`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").find_function("__init__").find_variable("self.body").is_equivalent("self.body = body")`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +--fcc-editable-region-- +class Email: + def __init__(self, sender, receiver, subject, body): + pass +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6852f06fdde1b4634a263f03.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6852f06fdde1b4634a263f03.md new file mode 100644 index 00000000000..9507351d3e2 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6852f06fdde1b4634a263f03.md @@ -0,0 +1,67 @@ +--- +id: 6852f06fdde1b4634a263f03 +title: Step 4 +challengeType: 20 +dashedName: step-4 +--- + +# --description-- + +Now let's test the `Email` class by creating an email object and checking that it stores information correctly. You'll print a couple of attributes to verify everything works. + +Create an email object named `email_obj` with `alice@example.com` as the sender, `bob@example.com` as the receiver, `Hello` as the subject, and `Hi Bob!` as the body. Then print the `sender` and `subject` attributes as separate print statements to verify they are stored correctly. + +# --hints-- + +You should create an email object named `email_obj` with the specified parameters. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_variable("email_obj").is_equivalent("email_obj = Email('alice@example.com', 'bob@example.com', 'Hello', 'Hi Bob!')")`)) + } + +}) +``` + +You should print `email_obj.sender`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).has_call("print(email_obj.sender)")`)) + } + +}) +``` + +You should print `email_obj.subject`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).has_call("print(email_obj.subject)")`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6852f0c3fcedb964ab959921.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6852f0c3fcedb964ab959921.md new file mode 100644 index 00000000000..bf73b8bdadc --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6852f0c3fcedb964ab959921.md @@ -0,0 +1,43 @@ +--- +id: 6852f0c3fcedb964ab959921 +title: Step 6 +challengeType: 20 +dashedName: step-6 +--- + +# --description-- + +Now let's test that the `read` attribute was added correctly to your `Email` class. Since you already have an email object from the previous steps, print the `read` attribute to see that it's now `False` by default. + +# --hints-- + +You should print `email_obj.read` to test the read attribute. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_call("print(email_obj.read)")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + +--fcc-editable-region-- +email_obj = Email('alice@example.com', 'bob@example.com', 'Hello', 'Hi Bob!') +print(email_obj.sender) +print(email_obj.subject) + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6852f11d412c29665985a915.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6852f11d412c29665985a915.md new file mode 100644 index 00000000000..5a224c6bb33 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6852f11d412c29665985a915.md @@ -0,0 +1,50 @@ +--- +id: 6852f11d412c29665985a915 +title: Step 12 +challengeType: 20 +dashedName: step-12 +--- + +# --description-- + +Now that users can send emails, they need a way to store emails they receive. Each user should have an inbox to collect their emails. + +Add an `inbox` attribute to the `User` class `__init__` method and initialize it as an empty list `[]`. This will store all emails that the user receives. + +# --hints-- + +You should add `self.inbox = []` to the `User` class `__init__` method. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("User").find_function("__init__").find_variable("self.inbox").is_equivalent("self.inbox = []")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- +class User: + def __init__(self, name): + self.name = name + +--fcc-editable-region-- + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f2da9b8ed891e9fdc5fc.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f2da9b8ed891e9fdc5fc.md new file mode 100644 index 00000000000..39cefda1beb --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f2da9b8ed891e9fdc5fc.md @@ -0,0 +1,76 @@ +--- +id: 6853f2da9b8ed891e9fdc5fc +title: Step 23 +challengeType: 20 +dashedName: step-23 +--- + +# --description-- + +Now let's display the sender and receiver information. To show who sent and received the email, add two print statements to the `display_full_email` method in this format: + +- `From: sender` where `sender` is replaced with the sender's name +- `To: receiver` where `receiver` is replaced with the receiver's name + +# --hints-- + +You should use f-strings to print the sender's name using `From: {self.sender.name}`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_call("print(f'From: {self.sender.name}')")`)); + } +}) +``` + +You should use f-strings to print the receiver's name using `To: {self.receiver.name}`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_call("print(f'To: {self.receiver.name}')")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f34ff93594934f6636d9.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f34ff93594934f6636d9.md new file mode 100644 index 00000000000..c69d6aa256b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f34ff93594934f6636d9.md @@ -0,0 +1,71 @@ +--- +id: 6853f34ff93594934f6636d9 +title: Step 27 +challengeType: 20 +dashedName: step-27 +--- + +# --description-- + +Let's add a string representation to our `Email` class so we can display brief email summaries. + +Add a `__str__` method to the `Email` class that takes `self` as a parameter. + +# --hints-- + +You should define a `__str__` method in the `Email` class. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").has_function("__str__")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + +--fcc-editable-region-- + +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f4584f508e95cc7a9f4d.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f4584f508e95cc7a9f4d.md new file mode 100644 index 00000000000..ffbcf6a49f8 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f4584f508e95cc7a9f4d.md @@ -0,0 +1,109 @@ +--- +id: 6853f4584f508e95cc7a9f4d +title: Step 30 +challengeType: 20 +dashedName: step-30 +--- + +# --description-- + +Now you'll create a method to list all emails in the inbox. This method should handle the case where the inbox is empty and display a numbered list of emails when there are emails present. + +Add a method called `list_emails` to your `Inbox` class that takes `self` as a parameter. First, create an `if` statement to check if the inbox is empty by testing the `emails` list. If it's empty, print the message `Your inbox is empty.\n`. Add a `return` statement to exit the method. + +# --hints-- + +You should define a method named `list_emails` in the `Inbox` class that takes `self` as a parameter. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("list_emails").has_args("self")`)) + } +}) +``` + +You should check if `self.emails` is empty using `if not self.emails`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("list_emails").find_ifs()[0].find_conditions()[0].is_equivalent("not self.emails")`)) + } +}) +``` + +You should print the message `Your inbox is empty.\n` if there are no emails. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("list_emails").find_ifs()[0].find_body()[0].is_equivalent("print('Your inbox is empty.\\\\n')")`)) + } +}) +``` + +You should have a `return` statement at the end of the `if` statement. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("list_emails").find_ifs()[0].find_body()[1].is_equivalent("return")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +--fcc-editable-region-- + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f4ded9f84896ff182dd8.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f4ded9f84896ff182dd8.md new file mode 100644 index 00000000000..a5d88497e4f --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f4ded9f84896ff182dd8.md @@ -0,0 +1,124 @@ +--- +id: 6853f4ded9f84896ff182dd8 +title: Step 31 +challengeType: 20 +dashedName: step-31 +--- + +# --description-- + +After the empty inbox check, print the message `\nYour Emails:`. After that, iterate over all emails in the inbox using a `for` loop with enumeration. This is the syntax for enumeration with a starting number: `enumerate(iterable, start=start_number)`. + +Use enumeration to number the emails starting from `1`. Use `i` (the index) and `email` (the email object) as the iteration variables. + +Inside the loop, print an f-string with the iteration index followed by a `.`, then a space and the string representation of the email object in this format: + +```md +i. string_representation +``` + +Where `i` is the index and `string_representation` is the representation of the email object. + +# --hints-- + +You should print the header `\nYour Emails:` outside the `if` statement. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("list_emails").find_body()[1].is_equivalent("print('\\\\nYour Emails:')")`)) + } +}) +``` + +You should have a `for` loop that iterates over `enumerate(self.emails, start=1)`. + +```js +({ + test: () => + { + runPython(` +_iter = _Node(_code).find_class("Inbox").find_function("list_emails").find_for_loops()[0].find_for_iter() +assert _iter.is_equivalent("enumerate(self.emails, start=1)") or _iter.is_equivalent("enumerate(self.emails, 1)") +`) + } +}) +``` + +Your `for` loop should use `i` and `email` to iterate over `enumerate(self.emails, start=1)`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("list_emails").find_for_loops()[0].find_for_vars().is_equivalent("i, email")`)) + } +}) +``` + +Inside the loop, you should print each email summary with a numbered prefix followed by the formatted string from the `__str__` method. Don't forget to add a `.` after the number. + +```js +({ + test: () => + { + assert(runPython(` + cond = _Node(_code).find_class("Inbox").find_function("list_emails").find_for_loops()[0].find_body() + cond.is_equivalent("print(f'{i}. {str(email)}')") or cond.is_equivalent("print(f'{i}. {email}')")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +--fcc-editable-region-- + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f553d90a2f9874ff9d0e.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f553d90a2f9874ff9d0e.md new file mode 100644 index 00000000000..63e9a070651 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f553d90a2f9874ff9d0e.md @@ -0,0 +1,103 @@ +--- +id: 6853f553d90a2f9874ff9d0e +title: Step 39 +challengeType: 20 +dashedName: step-39 +--- + +# --description-- + +Now we're ready to add timestamps to our emails to track when they were sent and received. + +First, import the `datetime` module at the top of your file. + +# --hints-- + +You should import the datetime module at the top of the file using `import datetime`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_import("import datetime")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +--fcc-editable-region-- + +--fcc-editable-region-- + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f68dd3affa9adb2e6d99.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f68dd3affa9adb2e6d99.md new file mode 100644 index 00000000000..76e061fbc1a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f68dd3affa9adb2e6d99.md @@ -0,0 +1,113 @@ +--- +id: 6853f68dd3affa9adb2e6d99 +title: Step 41 +challengeType: 20 +dashedName: step-41 +--- + +# --description-- + +Great! Now that you've practiced datetime formatting, remove the `current_time` variable and the print statement from the bottom of your code. We'll integrate timestamps into the `Email` class in the next step. + +# --hints-- + +You should not have the the statement `current_time = datetime.datetime.now()")` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_stmt("current_time = datetime.datetime.now()")`)); + } +}) +``` + +You should not have the practice print statement with `strftime()` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_call("print(current_time.strftime('%H:%M:%S'))")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +--fcc-editable-region-- +current_time = datetime.datetime.now() +print(current_time.strftime('%H:%M:%S')) +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f70470f45d9c280af659.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f70470f45d9c280af659.md new file mode 100644 index 00000000000..94be87c4115 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f70470f45d9c280af659.md @@ -0,0 +1,113 @@ +--- +id: 6853f70470f45d9c280af659 +title: Step 43 +challengeType: 20 +dashedName: step-43 +--- + +# --description-- + +Now let's show the timestamp when displaying the full email. + +Below the subject, print the received timestamp using `strftime` to format it as `'%Y-%m-%d %H:%M'`. Use the following format: + +```py +Received: date +``` + +Where `date` is formatted as `'%Y-%m-%d %H:%M'`. + + +# --hints-- + +You should print the formatted timestamp using `self.timestamp.strftime('%Y-%m-%d %H:%M')`. Add `Received:` followed by a space before it. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_call('print(f"Received: {self.timestamp.strftime(\\'%Y-%m-%d %H:%M\\')}")')`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + + print(f'Body: {self.body}') + print('------------\n') +--fcc-editable-region-- + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f90fd03acc9fd75ff860.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f90fd03acc9fd75ff860.md new file mode 100644 index 00000000000..ee6a984a575 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f90fd03acc9fd75ff860.md @@ -0,0 +1,138 @@ +--- +id: 6853f90fd03acc9fd75ff860 +title: Step 46 +challengeType: 20 +dashedName: step-46 +--- + +# --description-- + +Users should be able to check their inbox, read emails, and delete emails directly through their User object. + +For checking the inbox, add a method called `check_inbox` to the `User` class. This method should print a personalized header with the user's name and then display all their emails. + +The header should be formatted as: `\nName's Inbox:`, where `Name` is replaced with the name of the user. + +After printing the header, call the `list_emails()` method on the user's inbox. + +# --hints-- + +You should define a method named `check_inbox` in the `User` class that takes `self` as a parameter. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("User").find_function("check_inbox").has_args("self")`)) + } + +}) +``` + +Within `check_inbox`, you should print a header that includes the user's name in the format `\n[user]'s Inbox:`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("User").find_function("check_inbox").find_body()[0].is_equivalent('print(f"\\\\n{self.name}\\\'s Inbox:")')`)) + } +}) +``` + +Within `check_inbox`, you should call `self.inbox.list_emails()` to display the emails. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("User").find_function("check_inbox").find_body()[1].is_equivalent("self.inbox.list_emails()")`)) + } +}) + +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}" + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + print(f'Email sent from {self.name} to {receiver.name}!\n') + +--fcc-editable-region-- + +--fcc-editable-region-- + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6853f996671e63a0b4fbf143.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f996671e63a0b4fbf143.md new file mode 100644 index 00000000000..75a0221873b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6853f996671e63a0b4fbf143.md @@ -0,0 +1,141 @@ +--- +id: 6853f996671e63a0b4fbf143 +title: Step 47 +challengeType: 20 +dashedName: step-47 +--- + +# --description-- + +For reading and deleting emails, add two methods to your `User` class: `read_email` and `delete_email`. Both should take an `index` parameter and call the corresponding method on `self.inbox`. + +# --hints-- + +You should define a method named `read_email` in the `User` class that takes `self` and `index` as parameters. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("User").find_function("read_email").has_args("self, index")`)); + } +}) +``` + +The `read_email` method should call `self.inbox.read_email(index)`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("User").find_function("read_email").has_call("self.inbox.read_email(index)")`)); + } +}) +``` + +You should define a method named `delete_email` in the `User` class that takes `self` and `index` as parameters. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("User").find_function("delete_email").has_args("self, index")`)); + } +}) +``` + +The `delete_email` method should call `self.inbox.delete_email(index)`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("User").find_function("delete_email").has_call("self.inbox.delete_email(index)")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}" + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + print(f'Email sent from {self.name} to {receiver.name}!\n') + + def check_inbox(self): + print(f'\n{self.name}\'s Inbox:') + self.inbox.list_emails() + +--fcc-editable-region-- + +--fcc-editable-region-- + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685409f3f6c765aa4cd44a26.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685409f3f6c765aa4cd44a26.md new file mode 100644 index 00000000000..5ee56516d8d --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685409f3f6c765aa4cd44a26.md @@ -0,0 +1,134 @@ +--- +id: 685409f3f6c765aa4cd44a26 +title: Step 49 +challengeType: 20 +dashedName: step-49 +--- + +# --description-- + +Before sending the emails, add the standard Python idiom `if __name__ == '__main__':` followed by a call to `main()`. This ensures that the main function only runs when the script is executed directly, not when it's imported as a module. + +# --hints-- + +You should add the `if __name__ == '__main__':` check outside the `main` function. + + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_ifs()[0].find_conditions()[0].is_equivalent("__name__ == '__main__'")`)) + } + +}) +``` + +You should call `main()` inside the `if __name__ == '__main__':` block. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_ifs()[0].find_body().is_equivalent("main()")`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}" +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + print(f'Email sent from {self.name} to {receiver.name}!\n') + + def check_inbox(self): + print(f"\n{self.name}'s Inbox:") + self.inbox.list_emails() + + def read_email(self, index): + self.inbox.read_email(index) + + def delete_email(self, index): + self.inbox.delete_email(index) + +def main(): + tory = User('Tory') + ramy = User('Ramy') + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68540a534c56aeabae6afd58.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68540a534c56aeabae6afd58.md new file mode 100644 index 00000000000..216f0276234 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68540a534c56aeabae6afd58.md @@ -0,0 +1,140 @@ +--- +id: 68540a534c56aeabae6afd58 +title: Step 50 +challengeType: 20 +dashedName: step-50 +--- + +# --description-- + +Now you'll simulate sending some emails between the users. + +In your `main` function, after creating the users, use the `send_email` method to: + +- Have Tory send an email to the `ramy` user object with subject `Hello` and body `Hi Ramy, just saying hello!`. +- Have Ramy send an email to the `tory` user object with subject `Re: Hello` and body `Hi Tory, hope you are fine.`. + +# --hints-- + +You should have Tory send an email to `ramy`, subject `Hello`, and body `Hi Ramy, just saying hello!`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_function("main").find_body()[2].is_equivalent("tory.send_email(ramy, 'Hello', 'Hi Ramy, just saying hello!')")`)) + } + +}) +``` + +You should have Ramy send an email to `tory`, subject `Re: Hello`, and body `Hi Tory, hope you are fine.`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_function("main").find_body()[3].is_equivalent("ramy.send_email(tory, 'Re: Hello', 'Hi Tory, hope you are fine.')")`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}" +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + print(f'Email sent from {self.name} to {receiver.name}!\n') + + def check_inbox(self): + print(f"\n{self.name}'s Inbox:") + self.inbox.list_emails() + + def read_email(self, index): + self.inbox.read_email(index) + + def delete_email(self, index): + self.inbox.delete_email(index) + +--fcc-editable-region-- +def main(): + tory = User('Tory') + ramy = User('Ramy') + +--fcc-editable-region-- +if __name__ == '__main__': + main() +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68540aa7dfd449ad074c285c.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68540aa7dfd449ad074c285c.md new file mode 100644 index 00000000000..a7c5d9220e1 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68540aa7dfd449ad074c285c.md @@ -0,0 +1,287 @@ +--- +id: 68540aa7dfd449ad074c285c +title: Step 51 +challengeType: 20 +dashedName: step-51 +--- + +# --description-- + +Now you'll demonstrate the inbox functionality. Ramy will check his inbox, read the first email, delete the second email, and then check his inbox again to see the changes. + +In your `main` function, after sending the emails, add code to: + +- Have Ramy check his inbox using the `check_inbox` method. +- Have Ramy read the first email. +- Have Ramy delete the first email. +- Have Ramy check his inbox again. + +With this, you have completed the email simulator! + +# --hints-- + +You should call `ramy.check_inbox()` to show Ramy's emails. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_function("main").find_body().has_stmt("ramy.check_inbox()")`)) + } +}) +``` + +You should call `ramy.read_email(1)` to read the first email. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_function("main").find_body().has_stmt("ramy.read_email(1)")`)) + } +}) + +``` + +You should call `ramy.delete_email(1)` to delete the first email. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_function("main").find_body().has_stmt("ramy.delete_email(1)")`)) + } +}) + +``` + +You should call `ramy.check_inbox()` again to show the updated inbox. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_function("main").find_body().is_ordered("ramy.delete_email(1)", "ramy.check_inbox()")`)) + } +}) + +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}" +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + print(f'Email sent from {self.name} to {receiver.name}!\n') + + def check_inbox(self): + print(f"\n{self.name}'s Inbox:") + self.inbox.list_emails() + + def read_email(self, index): + self.inbox.read_email(index) + + def delete_email(self, index): + self.inbox.delete_email(index) + +--fcc-editable-region-- +def main(): + tory = User('Tory') + ramy = User('Ramy') + + tory.send_email(ramy, 'Hello', 'Hi Ramy, just saying hello!') + ramy.send_email(tory, 'Re: Hello', 'Hi Tory, hope you are fine.') + + +--fcc-editable-region-- +if __name__ == '__main__': + main() +``` + +# --solutions-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}" + + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + print(f'Email sent from {self.name} to {receiver.name}!\n') + + def check_inbox(self): + print(f"\n{self.name}'s Inbox:") + self.inbox.list_emails() + + def read_email(self, index): + self.inbox.read_email(index) + + def delete_email(self, index): + self.inbox.delete_email(index) + + +def main(): + # Create users + tory = User('Tory') + ramy = User('Ramy') + + # Send emails + tory.send_email(ramy, 'Hello', 'Hi Ramy, just saying hello!') + ramy.send_email(tory, 'Re: Hello', 'Hi Tory, hope you are fine.') + + # Ramy checks inbox + ramy.check_inbox() + + # Ramy reads first email (now uses 1-based indexing) + ramy.read_email(1) + + # Ramy deletes first email (now uses 1-based indexing) + ramy.delete_email(1) + + # Ramy checks inbox again + ramy.check_inbox() + + +if __name__ == '__main__': + main() +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68593ea22eac7e228ed585ce.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68593ea22eac7e228ed585ce.md new file mode 100644 index 00000000000..8435a7f9424 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68593ea22eac7e228ed585ce.md @@ -0,0 +1,42 @@ +--- +id: 68593ea22eac7e228ed585ce +title: Step 5 +challengeType: 20 +dashedName: step-5 +--- + +# --description-- + +Emails should track whether they've been read or not. Add a `read` attribute to the `__init__` method and set it to `False` by default, since new emails start as unread. + +# --hints-- + +You should assign `False` to ``self.read` in the `__init__` method. + +```js +({ + test: () => runPython(` + assert _Node(_code).find_class("Email").find_function("__init__").find_variable("self.read").is_equivalent("self.read = False") + `) +}) +``` + + +# --seed-- + +## --seed-contents-- + +```py +--fcc-editable-region-- +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + +--fcc-editable-region-- +email_obj = Email('alice@example.com', 'bob@example.com', 'Hello', 'Hi Bob!') +print(email_obj.sender) +print(email_obj.subject) +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6859400f8f7f872e50e1238c.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6859400f8f7f872e50e1238c.md new file mode 100644 index 00000000000..d3795be0013 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6859400f8f7f872e50e1238c.md @@ -0,0 +1,82 @@ +--- +id: 6859400f8f7f872e50e1238c +title: Step 11 +challengeType: 20 +dashedName: step-11 +--- + +# --description-- + +The `User` class needs a method to send emails to other users. When sending an email, you'll create a new `Email` object and add it to the receiver's inbox. + +Create a method called `send_email` in your `User` class. This method should take parameters for `receiver`, `subject`, and `body`. + +Inside the method, create a new `Email` object with the following parameter values and assign it to a variable named `email`: + +- `sender`: `self` (the current user) +- `receiver`: `receiver` (the user receiving the email) +- `subject`: `subject` (the subject of the email) +- `body`: `body` (the body of the email) + +# --hints-- + +You should define a `send_email` method in the `User` class. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("User").has_function("send_email")`)) + } + +}) +``` + +The `send_email` method should have parameters: `self`, `receiver`, `subject`, and `body`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("User").find_function("send_email").has_args("self, receiver, subject, body")`)) + } + +}) +``` + +The method should create an `Email` object named `email` with the correct parameters. + +```js +({ + test: () => { + assert(runPython(` +cond = _Node(_code).find_class("User").find_function("send_email").find_body()[0] +cond.is_equivalent("email = Email(sender=self, receiver=receiver, subject=subject, body=body)") or cond.is_equivalent("email = Email(self, receiver, subject, body)") + `)); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- +class User: + def __init__(self, name): + self.name = name + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685941422c30cd3dc41b1d0a.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685941422c30cd3dc41b1d0a.md new file mode 100644 index 00000000000..accbd0684a8 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685941422c30cd3dc41b1d0a.md @@ -0,0 +1,62 @@ +--- +id: 685941422c30cd3dc41b1d0a +title: Step 22 +challengeType: 20 +dashedName: step-22 +--- + +# --description-- + +Now let's add a header to the email display. In the `display_full_email` method, after calling `mark_as_read()`, print `\n--- Email ---` to start the email display with a clear header. + +# --hints-- + +You should print the header `\n--- Email ---` in the `display_full_email` method. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_call("print('\\\\n--- Email ---')")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- + def display_full_email(self): + self.mark_as_read() + +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68594df7779a164e6a61e8ae.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68594df7779a164e6a61e8ae.md new file mode 100644 index 00000000000..a061f7b44ec --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68594df7779a164e6a61e8ae.md @@ -0,0 +1,102 @@ +--- +id: 68594df7779a164e6a61e8ae +title: Step 32 +challengeType: 20 +dashedName: step-32 +--- + +# --description-- + +The inbox needs a method to read a specific email. When a user wants to read an email, they'll specify which email number (starting from index 0) they want to see, and the method will display the full email content. + +Add a method called `read_email` to your `Inbox` class that takes an `index` parameter. First, check if the inbox is empty and print the message `Inbox is empty.\n` if it is. Add a `return` statement after that to exit the method. + +# --hints-- + +You should define a method named `read_email` in the `Inbox` class that takes `self` and `index` as parameters. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").has_args("self, index")`)); + } +}) +``` + +You should check if `self.emails` is empty using `if not self.emails`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").find_ifs()[0].find_conditions()[0].is_equivalent("not self.emails")`)); + } +}) +``` + +You should print `Inbox is empty.\n` if there are no emails and return. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").find_ifs()[0].find_body()[0].is_equivalent("print('Inbox is empty.\\\\n')")`)); + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").find_ifs()[0].find_body()[1].is_equivalent("return")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6859523c793377706e19e9a0.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6859523c793377706e19e9a0.md new file mode 100644 index 00000000000..aef40cace73 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6859523c793377706e19e9a0.md @@ -0,0 +1,103 @@ +--- +id: 6859523c793377706e19e9a0 +title: Step 42 +challengeType: 20 +dashedName: step-42 +--- + +# --description-- + +Now let's add timestamps to our emails. In the Email class `__init__` method, create a `timestamp` attribute and assign the current time to it to automatically set a timestamp for when the email was created. + +This is helpful for tracking when messages were sent and received. + +# --hints-- + +You should have a `self.timestamp` in the Email `__init__` method and assign `datetime.datetime.now()` to it. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("__init__").has_stmt("self.timestamp = datetime.datetime.now()")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +--fcc-editable-region-- +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + +--fcc-editable-region-- + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685a82a54950823ef954276a.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685a82a54950823ef954276a.md new file mode 100644 index 00000000000..56169c830f9 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685a82a54950823ef954276a.md @@ -0,0 +1,145 @@ +--- +id: 685a82a54950823ef954276a +title: Step 48 +challengeType: 20 +dashedName: step-48 +--- + +# --description-- + +Now you'll create the main function that demonstrates your email simulator. + +Create the `main` function and inside it, using the `User` class, create two users: `Tory` and `Ramy` and assign them to variables `tory` and `ramy`, respectively. + +# --hints-- + +You should define a function named `main`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).has_function("main")`)) + } + +}) +``` + +You should create a user named `Tory` and assign it to a variable called `tory`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_function("main").find_variable("tory").is_equivalent("tory = User('Tory')")`)) + } + +}) +``` + +You should create a user named `Ramy` and assign it to a variable called `ramy`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_function("main").find_variable("ramy").is_equivalent("ramy = User('Ramy')")`)) + } + +}) + +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}" +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + print(f'Email sent from {self.name} to {receiver.name}!\n') + + def check_inbox(self): + print(f"\n{self.name}'s Inbox:") + self.inbox.list_emails() + + def read_email(self, index): + self.inbox.read_email(index) + + def delete_email(self, index): + self.inbox.delete_email(index) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685c1e32254e5993a5c8823e.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685c1e32254e5993a5c8823e.md new file mode 100644 index 00000000000..e086c9308d7 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685c1e32254e5993a5c8823e.md @@ -0,0 +1,71 @@ +--- +id: 685c1e32254e5993a5c8823e +title: Step 10 +challengeType: 20 +dashedName: step-10 +--- + +# --description-- + +Now that you have an initial setup of the `Email` class, it's time to create users who can send and receive emails. Each user will have a name and will be able to perform email operations. + +Create a `User` class with an `__init__` method that takes `self` and `name` as parameters. Within the class, assign the `name` parameter to `self.name`. + +# --hints-- + +You should create a `User` class. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).has_class("User")`)) + } + +}) +``` + +The `User` class should have an `__init__` method that takes `self` and `name` as parameters. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("User").find_function("__init__").has_args("self, name")`)) + } + +}) +``` + +You should assign the `name` parameter to `self.name`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("User").find_function("__init__").find_variable("self.name").is_equivalent("self.name = name")`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685c1eba966c6396850736ea.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685c1eba966c6396850736ea.md new file mode 100644 index 00000000000..40f5fb67613 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685c1eba966c6396850736ea.md @@ -0,0 +1,75 @@ +--- +id: 685c1eba966c6396850736ea +title: Step 13 +challengeType: 20 +dashedName: step-13 +--- + +# --description-- + +While users can send emails and have inboxes, we need a dedicated class to manage inbox operations efficiently. + +The `Inbox` class will store a list of emails and provide methods to add new emails, list all emails, and manage them. + +Create a new class called `Inbox` with an `__init__` method that initializes an empty list called `emails`. + +# --hints-- + +You should create an `Inbox` class. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_class("Inbox")`)); + } +}) +``` + +The `Inbox` class should have an `__init__` method that takes `self` as a parameter. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("__init__").has_args("self")`)); + } +}) +``` + +You should initialize `self.emails = []` in the `Inbox` class `__init__` method. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("__init__").find_variable("self.emails").is_equivalent("self.emails = []")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +class User: + def __init__(self, name): + self.name = name + self.inbox = [] + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685cdabd4cded3b340193f72.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685cdabd4cded3b340193f72.md new file mode 100644 index 00000000000..3e2c47b0385 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685cdabd4cded3b340193f72.md @@ -0,0 +1,67 @@ +--- +id: 685cdabd4cded3b340193f72 +title: Step 24 +challengeType: 20 +dashedName: step-24 +--- + +# --description-- + +Now add the subject line to the email display. In the `display_full_email` method, add a print statement to show the email's subject in this format: + +- `Subject: subject` where `subject` is replaced with the subject of the email + +# --hints-- + +You should use f-strings to print the subject using `Subject: {self.subject}`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_call("print(f'Subject: {self.subject}')")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685cdfd1932d8ac6ad986813.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685cdfd1932d8ac6ad986813.md new file mode 100644 index 00000000000..9fe7a1376e4 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685cdfd1932d8ac6ad986813.md @@ -0,0 +1,91 @@ +--- +id: 685cdfd1932d8ac6ad986813 +title: Step 28 +challengeType: 20 +dashedName: step-28 +--- + +# --description-- + +Within the `__str__` method, you'll show whether an email has been read or not. + +Conditional expressions allow you to assign a value based on a condition in a single line. + +Here is an example: + +```py +x = 10 +y = 'Even' if x % 2 == 0 else 'Odd' # y will be even +``` + +Within the method, before, use conditional expression to assign the string `Read` to a variable `status` if the email is read and `Unread` if it is not. + +# --hints-- + +The `__str__` method should create a `status` variable that uses a conditional expression. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("__str__").has_stmt("status = 'Read' if self.read else 'Unread'")`)); + } +}) +``` + +The `__str__` method should return the email information with the status included. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("__str__").has_variable("status")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + +--fcc-editable-region-- + def __str__(self): + pass +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685d2fd1f309b9191f257c55.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685d2fd1f309b9191f257c55.md new file mode 100644 index 00000000000..35395e53213 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685d2fd1f309b9191f257c55.md @@ -0,0 +1,125 @@ +--- +id: 685d2fd1f309b9191f257c55 +title: Step 40 +challengeType: 20 +dashedName: step-40 +--- + +# --description-- + +Before integrating timestamps into our email system, let's practice working with datetime formatting. The `datetime.datetime.now()` function gives us the current date and time, and we can use the `strftime()` method to format it in different ways. + +Here's how `strftime()` works with format codes: + +```python +now = datetime.datetime.now() +print(now.strftime("%Y-%m-%d")) # Output: 2024-03-15 (year-month-day with - separator) +``` + +The format codes like `%Y` (year), `%m` (month), `%d` (day) tell `strftime()` what to include, and you can add separators like `-` between them. + +At the bottom of your code, create a variable called `current_time` and assign it `datetime.datetime.now()`. Then use `strftime()` to print the time in hours:minutes:seconds format using `:` as the separator. + +Use these format codes: `%H` for hours (24-hour format), `%M` for minutes, and `%S` for seconds. + +# --hints-- + +You should create a variable called `current_time` and assign it `datetime.datetime.now()`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_stmt("current_time = datetime.datetime.now()")`)); + } +}) +``` + +You should use `strftime()` to print the time in `%H:%M:%S` format. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_call("print(current_time.strftime('%H:%M:%S'))")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685d53b5e4f5784562a12d1b.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685d53b5e4f5784562a12d1b.md new file mode 100644 index 00000000000..732ac244c28 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685d53b5e4f5784562a12d1b.md @@ -0,0 +1,107 @@ +--- +id: 685d53b5e4f5784562a12d1b +title: Step 45 +challengeType: 20 +dashedName: step-45 +--- + +# --description-- + +Users should get confirmation when they successfully send an email. Let's improve the user experience by adding feedback to the `send_email` method. + +In the `send_email` method of the `User` class, add a print statement after the email is sent that shows confirmation. The message should include both the sender's name and the receiver's name in this format: `Email sent from [sender_name] to [receiver_name]!\n` + +# --hints-- + +You should add a print statement in the `send_email` method that confirms the email was sent. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("User").find_function("send_email").has_call("print(f'Email sent from {self.name} to {receiver.name}!\\\\n')")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}" + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + +--fcc-editable-region-- + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +--fcc-editable-region-- + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') +``` + diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/685d53ec88a16a4643c2437b.md b/curriculum/challenges/english/blocks/workshop-email-simulator/685d53ec88a16a4643c2437b.md new file mode 100644 index 00000000000..0478d9df302 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/685d53ec88a16a4643c2437b.md @@ -0,0 +1,113 @@ +--- +id: 685d53ec88a16a4643c2437b +title: Step 44 +challengeType: 20 +dashedName: step-44 +--- + +# --description-- + +Now let's also show timestamps in the email listing. Update the `__str__` method in the `Email` class to include the timestamp after the subject. + +Modify the return statement to include the timestamp formatted as `'%Y-%m-%d %H:%M'` at the end, separated by ` | Time: `. + +The complete format should be: + +```py +[status] From: sender | Subject: subject | Time: time +``` + +Where `status` is the status of the email, `sender` is the sender's name, `subject` is the subject of the email, and `time` is in the format `'%Y-%m-%d %H:%M'`. + +# --hints-- + +You should update the `__str__` method to include the formatted timestamp. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("__str__").has_stmt("return f\\"[{status}] From: {self.sender.name} | Subject: {self.subject} | Time: {self.timestamp.strftime('%Y-%m-%d %H:%M')}\\"")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +import datetime + +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.timestamp = datetime.datetime.now() + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f"Received: {self.timestamp.strftime('%Y-%m-%d %H:%M')}") + print(f'Body: {self.body}') + print('------------\n') + +--fcc-editable-region-- + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + del self.emails[actual_index] + print('Email deleted.\n') +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/688754d6d59578df1313cb3c.md b/curriculum/challenges/english/blocks/workshop-email-simulator/688754d6d59578df1313cb3c.md new file mode 100644 index 00000000000..42e48c3d13c --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/688754d6d59578df1313cb3c.md @@ -0,0 +1,58 @@ +--- +id: 688754d6d59578df1313cb3c +title: Step 14 +challengeType: 20 +dashedName: step-14 +--- + +# --description-- + +Now it's time to connect the User and Inbox classes so that emails can actually be delivered! + +Users need to have proper Inbox objects instead of simple lists, and the `send_email` method should deliver emails to the receiver's inbox. + +Update the `User` class `__init__` in a way that each user gets an actual Inbox object instead of an empty list created earlier. + +# --hints-- + +You should update the User class to initialize `self.inbox = Inbox()`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("User").find_function("__init__").find_variable("self.inbox").is_equivalent("self.inbox = Inbox()")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- +class User: + def __init__(self, name): + self.name = name + self.inbox = [] +--fcc-editable-region-- + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + +class Inbox: + def __init__(self): + self.emails = [] + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/688754f3494b17e0b352d67d.md b/curriculum/challenges/english/blocks/workshop-email-simulator/688754f3494b17e0b352d67d.md new file mode 100644 index 00000000000..e65b10e5523 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/688754f3494b17e0b352d67d.md @@ -0,0 +1,71 @@ +--- +id: 688754f3494b17e0b352d67d +title: Step 17 +challengeType: 20 +dashedName: step-17 +--- + +# --description-- + +Now it's time to test our complete email system! Let's create some users and see the email functionality in action. + +Create two User objects: `alice` with name `"Alice"` and `bob` with name `"Bob"`. This will demonstrate how users can be created and interact with each other. + +# --hints-- + +You should create a User object named `alice` with name `"Alice"`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_variable("alice").is_equivalent('alice = User("Alice")')`)); + } +}) +``` + +You should create a User object named `bob` with name `"Bob"`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_variable("bob").is_equivalent('bob = User("Bob")')`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/6887550fe36f64e27074475a.md b/curriculum/challenges/english/blocks/workshop-email-simulator/6887550fe36f64e27074475a.md new file mode 100644 index 00000000000..b4e8825e50a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/6887550fe36f64e27074475a.md @@ -0,0 +1,63 @@ +--- +id: 6887550fe36f64e27074475a +title: Step 18 +challengeType: 20 +dashedName: step-18 +--- + +# --description-- + +Have Alice send an email to Bob to see if the email delivery works correctly. + +Use Alice's `send_email` method to send Bob an email with subject `"Hello"` and body `"Hi Bob, how are you?"`. + +# --hints-- + +You should call `alice.send_email()` with the correct parameters. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_call('alice.send_email(bob, "Hello", "Hi Bob, how are you?")')`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +--fcc-editable-region-- +alice = User("Alice") +bob = User("Bob") + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68875521efc1a4e3f0c570e3.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68875521efc1a4e3f0c570e3.md new file mode 100644 index 00000000000..543cf1c56ce --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68875521efc1a4e3f0c570e3.md @@ -0,0 +1,63 @@ +--- +id: 68875521efc1a4e3f0c570e3 +title: Step 19 +challengeType: 20 +dashedName: step-19 +--- + +# --description-- + +Now let's verify that the email was delivered successfully by printing the length of Bob's inbox emails. + +# --hints-- + +You should print the length of Bob's inbox emails using `len(bob.inbox.emails)` + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_call("print(len(bob.inbox.emails))")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +--fcc-editable-region-- +alice = User("Alice") +bob = User("Bob") + +alice.send_email(bob, "Hello", "Hi Bob, how are you?") + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68875554c6c32de788afc64e.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68875554c6c32de788afc64e.md new file mode 100644 index 00000000000..decd8367052 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68875554c6c32de788afc64e.md @@ -0,0 +1,83 @@ +--- +id: 68875554c6c32de788afc64e +title: Step 21 +challengeType: 20 +dashedName: step-21 +--- + +# --description-- + +Users need a way to read their emails properly. + +Add a `display_full_email` method to the `Email` class that takes only `self` as a parameter. Inside this method, call `mark_as_read()` method to mark the email as read when someone views it. + +This method would be used to display the email's full content in a nicely formatted way. + +# --hints-- + +You should define a `display_full_email` method in the `Email` class. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").has_function("display_full_email")`)); + } +}) +``` + +The `display_full_email` method should take only `self` as a parameter. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_args("self")`)); + } +}) +``` + +The method should call `self.mark_as_read()` to mark the email as read. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_call("self.mark_as_read()")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True +--fcc-editable-region-- + +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/688798fccea91f522d1f245e.md b/curriculum/challenges/english/blocks/workshop-email-simulator/688798fccea91f522d1f245e.md new file mode 100644 index 00000000000..a0d37a9dc06 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/688798fccea91f522d1f245e.md @@ -0,0 +1,90 @@ +--- +id: 688798fccea91f522d1f245e +title: Step 33 +challengeType: 20 +dashedName: step-33 +--- + +# --description-- + +When the inbox is not empty, you'll try to access the email at the given index and display it. + +Remember in the `list_emails` method, you displayed email numbers starting from 1, but list indices in Python start from 0. So, you'll need to convert the 1-based index to a 0-based index by subtracting 1. + +Within the `read_email` method, subtract 1 from the `index` parameter and store it in a variable called `actual_index`. + + +# --hints-- + +You should subtract 1 from the `index` parameter and store it in a variable called `actual_index`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").find_body().find_variable("actual_index").is_equivalent("actual_index = index - 1")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + +--fcc-editable-region-- + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68879d0a2aeb368e26559635.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68879d0a2aeb368e26559635.md new file mode 100644 index 00000000000..0a013329c6e --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68879d0a2aeb368e26559635.md @@ -0,0 +1,67 @@ +--- +id: 68879d0a2aeb368e26559635 +title: Step 26 +challengeType: 20 +dashedName: step-26 +--- + +# --description-- + +Finally, let's add a footer to complete the email display format. Add a final print statement: `print('------------\n')` to close off the email display with a nice separator line. + +# --hints-- + +You should print the footer `------------\n` at the end of the `display_full_email` method. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_call("print('------------\\\\n')")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68879d6de6398893be8c4b7b.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68879d6de6398893be8c4b7b.md new file mode 100644 index 00000000000..27f36f61acd --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68879d6de6398893be8c4b7b.md @@ -0,0 +1,66 @@ +--- +id: 68879d6de6398893be8c4b7b +title: Step 25 +challengeType: 20 +dashedName: step-25 +--- + +# --description-- + +Now add the email body to complete the main content. In the `display_full_email` method, add another print statement in the format `Body: body` (where `body` is the content of the email) to show the email's content. + +# --hints-- + +You should print the email body using `f'Body: {self.body}'`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("display_full_email").has_call("print(f'Body: {self.body}')")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68b930ee0e7b8fb81895e59a.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68b930ee0e7b8fb81895e59a.md new file mode 100644 index 00000000000..c8c5357921f --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68b930ee0e7b8fb81895e59a.md @@ -0,0 +1,60 @@ +--- +id: 68b930ee0e7b8fb81895e59a +title: Step 7 +challengeType: 20 +dashedName: step-7 +--- + +# --description-- + +Now you'll add a method to mark an email as read. When someone opens an email, you want to change its status from unread to read. This method will be simple - it just needs to set the `read` attribute to `True`. + +Add a method called `mark_as_read` to your `Email` class. This method should take only `self` as a parameter and set the `read` attribute to `True`. + +# --hints-- + +You should define a method named `mark_as_read` in the `Email` class. +that should only take `self` as a parameter. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").find_function("mark_as_read").has_args("self")`)) + } + +}) +``` + +The method should set `self.read` to `True`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Email").find_function("mark_as_read").find_variable("self.read").is_equivalent("self.read = True")`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +--fcc-editable-region-- +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + +--fcc-editable-region-- +email_obj = Email('alice@example.com', 'bob@example.com', 'Hello', 'Hi Bob!') +print(email_obj.sender) +print(email_obj.subject) +print(email_obj.read) +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68b9350183ff05bd106c79e4.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68b9350183ff05bd106c79e4.md new file mode 100644 index 00000000000..41556ce975d --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68b9350183ff05bd106c79e4.md @@ -0,0 +1,61 @@ +--- +id: 68b9350183ff05bd106c79e4 +title: Step 8 +challengeType: 20 +dashedName: step-8 +--- + +# --description-- + +Now, test the `mark_as_read` method you created in the `Email` class. Use the method on the existing `Email` object `email_obj` to change the status. After marking the email as read, print the `read` attribute of `email_obj` to confirm the change. + +# --hints-- + +You should call the `mark_as_read` method on the `email_obj` to mark the email as read. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).has_call("email_obj.mark_as_read()")`)) + } + +}) +``` + +You should print the `read` attribute of the `email_obj` after calling the `mark_as_read` method. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).is_ordered("email_obj.mark_as_read()", "print(email_obj.read)")`)) + } + +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- +email_obj = Email('alice@example.com', 'bob@example.com', 'Hello', 'Hi Bob!') +print(email_obj.sender) +print(email_obj.subject) +print(email_obj.read) + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68b9366fae8b13c1278761b1.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68b9366fae8b13c1278761b1.md new file mode 100644 index 00000000000..b1089bb3c6b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68b9366fae8b13c1278761b1.md @@ -0,0 +1,69 @@ +--- +id: 68b9366fae8b13c1278761b1 +title: Step 15 +challengeType: 20 +dashedName: step-15 +--- + +# --description-- + +Your inbox needs a way to receive new emails. When someone sends an email to a user, it should be added to their inbox. + +Add a method called `receive_email` to your `Inbox` class. that takes `self` and an email object `email` as a parameter. Within the method body, add the `email` to the `emails` list using the `append` method. + +# --hints-- + +You should define a method named `receive_email` in the `Inbox` class that takes `self` and `email` as parameters. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("receive_email").has_args("self, email")`)) + } +}) +``` + +You should append the `email` to `self.emails`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("receive_email").has_stmt("self.emails.append(email)")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + +--fcc-editable-region-- +class Inbox: + def __init__(self): + self.emails = [] + + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68b936bf4f47d6c29a669f99.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68b936bf4f47d6c29a669f99.md new file mode 100644 index 00000000000..8ba4af04ac0 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68b936bf4f47d6c29a669f99.md @@ -0,0 +1,57 @@ +--- +id: 68b936bf4f47d6c29a669f99 +title: Step 16 +challengeType: 20 +dashedName: step-16 +--- + +# --description-- + +Modify the `send_email` method to add the email to the receiver's inbox by calling the `receive_email` method of the `Inbox` class. + +# --hints-- + +You should add `receiver.inbox.receive_email(email)` to the `send_email` method. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("User").find_function("send_email").has_stmt("receiver.inbox.receive_email(email)")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + +--fcc-editable-region-- + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + +--fcc-editable-region-- + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68b96d6a290d52d5e57a7485.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68b96d6a290d52d5e57a7485.md new file mode 100644 index 00000000000..c0d5b7c5f02 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68b96d6a290d52d5e57a7485.md @@ -0,0 +1,88 @@ +--- +id: 68b96d6a290d52d5e57a7485 +title: Step 9 +challengeType: 20 +dashedName: step-9 +--- + +# --description-- + +Remove the `email_obj` and the following print statements. + +# --hints-- + +You should not have the `email_obj` creation line in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_stmt("email_obj = Email('alice@example.com', 'bob@example.com', 'Hello', 'Hi Bob!')")`)); + } +}) +``` + +You should not have `print(email_obj.sender)` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_call("print(email_obj.sender)")`)); + } +}) +``` + +You should not have `print(email_obj.subject)` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_call("print(email_obj.subject)")`)); + } +}) +``` + +You should not have `print(email_obj.read)` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_call("print(email_obj.read)")`)); + } +}) +``` + +You should not have `email_obj.mark_as_read()` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_call("email_obj.mark_as_read()")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +--fcc-editable-region-- +email_obj = Email('alice@example.com', 'bob@example.com', 'Hello', 'Hi Bob!') +print(email_obj.sender) +print(email_obj.subject) +print(email_obj.read) +email_obj.mark_as_read() +print(email_obj.read) +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68b9a011daf902ff1eba40a8.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68b9a011daf902ff1eba40a8.md new file mode 100644 index 00000000000..c2d50c710ee --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68b9a011daf902ff1eba40a8.md @@ -0,0 +1,94 @@ +--- +id: 68b9a011daf902ff1eba40a8 +title: Step 20 +challengeType: 20 +dashedName: step-20 +--- + +# --description-- + +Now that the email has been received, remove object creations, method calls, and print statements. + +# --hints-- + +You should not have `alice = User("Alice")` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_stmt("alice = User(\\"Alice\\")")`)); + } +}) +``` + +You should not have `bob = User("Bob")` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_stmt("bob = User(\\"Bob\\")")`)); + } +}) +``` + +You should not have `alice.send_email(bob, "Hello", "Hi Bob, how are you?")` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_call("alice.send_email(bob, \\"Hello\\", \\"Hi Bob, how are you?\\")")`)); + } +}) +``` + +You should not have `print(len(bob.inbox.emails))` in your code. + +```js +({ + test: () => { + assert(!runPython(`_Node(_code).has_call("print(len(bob.inbox.emails))")`)); + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +--fcc-editable-region-- +alice = User("Alice") +bob = User("Bob") + +alice.send_email(bob, "Hello", "Hi Bob, how are you?") + +print(len(bob.inbox.emails)) +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68be6a4447e61c4395ef2359.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68be6a4447e61c4395ef2359.md new file mode 100644 index 00000000000..45d3417721b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68be6a4447e61c4395ef2359.md @@ -0,0 +1,119 @@ +--- +id: 68be6a4447e61c4395ef2359 +title: Step 36 +challengeType: 20 +dashedName: step-36 +--- + +# --description-- + +Your inbox also needs a way to delete emails. + +Python's `del` keyword can be used to delete items from a list by their index. + +Add a method called `delete_email` to your `Inbox` class. Like `read_email`, it should: + +- Take an `index` parameter, check for an empty inbox, and print the message `Inbox is empty.\n` if it is. Use `return` to exit the method. + +# --hints-- + +You should define a method named `delete_email` in the `Inbox` class that takes `self` and `index` as parameters. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").has_args("self,index")`)) + } +}) + +``` + +You should check if the inbox is empty first using `if not self.emails:`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").find_ifs()[0].find_conditions()[0].is_equivalent("not self.emails")`)) + } +}) + +``` + +If inbox is empty, you should print the message `Inbox is empty.\n` and use `return` to exit the method. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").find_ifs()[0].find_body().is_equivalent("print('Inbox is empty.\\\\n')\\nreturn")`)) +} +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68be6af1f43cc44541debe41.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68be6af1f43cc44541debe41.md new file mode 100644 index 00000000000..d986d17258e --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68be6af1f43cc44541debe41.md @@ -0,0 +1,125 @@ +--- +id: 68be6af1f43cc44541debe41 +title: Step 37 +challengeType: 20 +dashedName: step-37 +--- + +# --description-- + +You should handle the case where the user tries to delete an email using an invalid index just like you did in the `read_email` method. + +Within the `delete_email` method: + +- Convert the 1-based `index` parameter to a 0-based index by subtracting 1 and storing it in a variable called `actual_index`. +- Create an if statement to check if the `actual_index` is less than 0 or greater than or equal to the length of the `self.emails` list. If it is, print the message `Invalid email number.\n` and use `return` to exit the method. + +# --hints-- + + +You should subtract 1 from the `index` parameter and store it in a variable called `actual_index`. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").find_body().find_variable("actual_index").is_equivalent("actual_index = index - 1")`)) + } +}) +``` + +You should create an if statement to check if the `actual_index` is less than 0 or greater than or equal to the length of the `self.emails` list. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").find_ifs()[1].find_conditions()[0].is_equivalent("actual_index < 0 or actual_index >= len(self.emails)")`)) + } +}) +``` + +Within the `if` statememt, you should print the message `Invalid email number.\n` and use `return` to exit the method. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").find_ifs()[1].find_body()[0].is_equivalent("print('Invalid email number.\\\\n')")`)) + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").find_ifs()[1].find_body()[1].is_equivalent("return")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + +--fcc-editable-region-- + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68bf0f09b7ec33be465c0c35.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68bf0f09b7ec33be465c0c35.md new file mode 100644 index 00000000000..abe732fb3b4 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68bf0f09b7ec33be465c0c35.md @@ -0,0 +1,76 @@ +--- +id: 68bf0f09b7ec33be465c0c35 +title: Step 29 +challengeType: 20 +dashedName: step-29 +--- + +# --description-- + +At the end of the `__str__` method, return the email summary in this format: + +```py +[status] From: sender | Subject: subject. +``` + +Where `status` is the status of the email, `sender` is the sender's name and `subject` is the subject of the email. + +# --hints-- + +The `__str__` method should return `f"[{status}] From: {self.sender.name} | Subject: {self.subject}"`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).find_class("Email").find_function("__str__").has_return("f\\"[{status}] From: {self.sender.name} | Subject: {self.subject}\\"")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + +--fcc-editable-region-- + def __str__(self): + status = 'Read' if self.read else 'Unread' +--fcc-editable-region-- + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc5082ea4b4cdcf09dd58.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc5082ea4b4cdcf09dd58.md new file mode 100644 index 00000000000..470c45a91d6 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc5082ea4b4cdcf09dd58.md @@ -0,0 +1,99 @@ +--- +id: 68dfc5082ea4b4cdcf09dd58 +title: Step 34 +challengeType: 20 +dashedName: step-34 +--- + +# --description-- + +After getting the actual index, create an if statement to check if the `actual_index` is less than 0 or greater than or equal to the length of the `self.emails` list. If it is, print the message `Invalid email number.\n` and use `return` to exit the method. + +# --hints-- + +You should create an if statement to check if the `actual_index` is less than 0 or greater than or equal to the length of the `self.emails` list. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").find_ifs()[1].find_conditions()[0].is_equivalent("actual_index < 0 or actual_index >= len(self.emails)")`)) + } +}) + +``` + +If the index is invalid, you should print the message `Invalid email number.\n` and use `return` to exit the method. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").find_ifs()[1].find_body()[0].is_equivalent("print('Invalid email number.\\\\n')")`)) + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").find_ifs()[1].find_body()[1].is_equivalent("return")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + +--fcc-editable-region-- + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc5f71dc2a6d65091a39c.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc5f71dc2a6d65091a39c.md new file mode 100644 index 00000000000..31feb7a3edf --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc5f71dc2a6d65091a39c.md @@ -0,0 +1,89 @@ +--- +id: 68dfc5f71dc2a6d65091a39c +title: Step 35 +challengeType: 20 +dashedName: step-35 +--- + +# --description-- + +When the email index is valid, access the email at `actual_index`, call its `display_full_email` method to show the email content. + +# --hints-- + +You should access the email at `actual_index` in the `self.emails` list and call its `display_full_email` method. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("read_email").find_body().has_stmt("self.emails[actual_index].display_full_email()")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + +--fcc-editable-region-- + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + +--fcc-editable-region-- + +``` diff --git a/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc80941fa2ef744c64cd6.md b/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc80941fa2ef744c64cd6.md new file mode 100644 index 00000000000..18dc9b08d0c --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-email-simulator/68dfc80941fa2ef744c64cd6.md @@ -0,0 +1,111 @@ +--- +id: 68dfc80941fa2ef744c64cd6 +title: Step 38 +challengeType: 20 +dashedName: step-38 +--- + +# --description-- + +When the inbox is not empty, and the email index is valid, delete the email at the given index using the `del` keyword and print a confirmation message `Email deleted.\n`. + +# --hints-- + +You should use the `del` keyword to delete the email at `actual_index` in the `self.emails` list. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").find_body().has_stmt("del self.emails[actual_index]")`)) + } +}) +``` + +You should print the message `Email deleted.\n` after deleting the email. + +```js +({ + test: () => + { + assert(runPython(`_Node(_code).find_class("Inbox").find_function("delete_email").find_body().has_call("print('Email deleted.\\\\n')")`)) + } +}) +``` + +# --seed-- + +## --seed-contents-- + +```py +class Email: + def __init__(self, sender, receiver, subject, body): + self.sender = sender + self.receiver = receiver + self.subject = subject + self.body = body + self.read = False + + def mark_as_read(self): + self.read = True + + def display_full_email(self): + self.mark_as_read() + print('\n--- Email ---') + print(f'From: {self.sender.name}') + print(f'To: {self.receiver.name}') + print(f'Subject: {self.subject}') + print(f'Body: {self.body}') + print('------------\n') + + def __str__(self): + status = 'Read' if self.read else 'Unread' + return f"[{status}] From: {self.sender.name} | Subject: {self.subject}" + +class User: + def __init__(self, name): + self.name = name + self.inbox = Inbox() + + def send_email(self, receiver, subject, body): + email = Email(sender=self, receiver=receiver, subject=subject, body=body) + receiver.inbox.receive_email(email) + +class Inbox: + def __init__(self): + self.emails = [] + + def receive_email(self, email): + self.emails.append(email) + + def list_emails(self): + if not self.emails: + print('Your inbox is empty.\n') + return + print('\nYour Emails:') + for i, email in enumerate(self.emails, start=1): + print(f'{i}. {email}') + + def read_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + self.emails[actual_index].display_full_email() + +--fcc-editable-region-- + def delete_email(self, index): + if not self.emails: + print('Inbox is empty.\n') + return + actual_index = index - 1 + if actual_index < 0 or actual_index >= len(self.emails): + print('Invalid email number.\n') + return + +--fcc-editable-region-- + +``` diff --git a/curriculum/structure/blocks/workshop-email-simulator.json b/curriculum/structure/blocks/workshop-email-simulator.json new file mode 100644 index 00000000000..b249ff9877e --- /dev/null +++ b/curriculum/structure/blocks/workshop-email-simulator.json @@ -0,0 +1,63 @@ +{ + "name": "Build an Email Simulator", + "isUpcomingChange": true, + "dashedName": "workshop-email-simulator", + "helpCategory": "Python", + "blockLayout": "challenge-grid", + "blockType": "workshop", + "challengeOrder": [ + { "id": "6852ea0c3cadb54e897acdac", "title": "Step 1" }, + { "id": "6852ef95ccc27660275692cd", "title": "Step 2" }, + { "id": "6852f01116912861c7d1b0e6", "title": "Step 3" }, + { "id": "6852f06fdde1b4634a263f03", "title": "Step 4" }, + { "id": "68593ea22eac7e228ed585ce", "title": "Step 5" }, + { "id": "6852f0c3fcedb964ab959921", "title": "Step 6" }, + { "id": "68b930ee0e7b8fb81895e59a", "title": "Step 7" }, + { "id": "68b9350183ff05bd106c79e4", "title": "Step 8" }, + { "id": "68b96d6a290d52d5e57a7485", "title": "Step 9" }, + { "id": "685c1e32254e5993a5c8823e", "title": "Step 10" }, + { "id": "6859400f8f7f872e50e1238c", "title": "Step 11" }, + { "id": "6852f11d412c29665985a915", "title": "Step 12" }, + { "id": "685c1eba966c6396850736ea", "title": "Step 13" }, + { "id": "688754d6d59578df1313cb3c", "title": "Step 14" }, + { "id": "68b9366fae8b13c1278761b1", "title": "Step 15" }, + { "id": "68b936bf4f47d6c29a669f99", "title": "Step 16" }, + { "id": "688754f3494b17e0b352d67d", "title": "Step 17" }, + { "id": "6887550fe36f64e27074475a", "title": "Step 18" }, + { "id": "68875521efc1a4e3f0c570e3", "title": "Step 19" }, + { "id": "68b9a011daf902ff1eba40a8", "title": "Step 20" }, + { "id": "68875554c6c32de788afc64e", "title": "Step 21" }, + { "id": "685941422c30cd3dc41b1d0a", "title": "Step 22" }, + { "id": "6853f2da9b8ed891e9fdc5fc", "title": "Step 23" }, + { "id": "685cdabd4cded3b340193f72", "title": "Step 24" }, + { "id": "68879d6de6398893be8c4b7b", "title": "Step 25" }, + { "id": "68879d0a2aeb368e26559635", "title": "Step 26" }, + { "id": "6853f34ff93594934f6636d9", "title": "Step 27" }, + { "id": "685cdfd1932d8ac6ad986813", "title": "Step 28" }, + { "id": "68bf0f09b7ec33be465c0c35", "title": "Step 29" }, + { "id": "6853f4584f508e95cc7a9f4d", "title": "Step 30" }, + { "id": "6853f4ded9f84896ff182dd8", "title": "Step 31" }, + { "id": "68594df7779a164e6a61e8ae", "title": "Step 32" }, + { "id": "688798fccea91f522d1f245e", "title": "Step 33" }, + { "id": "68dfc5082ea4b4cdcf09dd58", "title": "Step 34" }, + { "id": "68dfc5f71dc2a6d65091a39c", "title": "Step 35" }, + { "id": "68be6a4447e61c4395ef2359", "title": "Step 36" }, + { "id": "68be6af1f43cc44541debe41", "title": "Step 37" }, + { "id": "68dfc80941fa2ef744c64cd6", "title": "Step 38" }, + { "id": "6853f553d90a2f9874ff9d0e", "title": "Step 39" }, + { "id": "685d2fd1f309b9191f257c55", "title": "Step 40" }, + { "id": "6853f68dd3affa9adb2e6d99", "title": "Step 41" }, + { "id": "6859523c793377706e19e9a0", "title": "Step 42" }, + { "id": "6853f70470f45d9c280af659", "title": "Step 43" }, + { "id": "685d53ec88a16a4643c2437b", "title": "Step 44" }, + { "id": "685d53b5e4f5784562a12d1b", "title": "Step 45" }, + { "id": "6853f90fd03acc9fd75ff860", "title": "Step 46" }, + { "id": "6853f996671e63a0b4fbf143", "title": "Step 47" }, + { "id": "685a82a54950823ef954276a", "title": "Step 48" }, + { "id": "685409f3f6c765aa4cd44a26", "title": "Step 49" }, + { "id": "68540a534c56aeabae6afd58", "title": "Step 50" }, + { "id": "68540aa7dfd449ad074c285c", "title": "Step 51" } + ], + "usesMultifileEditor": true, + "hasEditableBoundaries": true +} diff --git a/curriculum/structure/superblocks/full-stack-developer.json b/curriculum/structure/superblocks/full-stack-developer.json index c4de51db39d..170de322a9d 100644 --- a/curriculum/structure/superblocks/full-stack-developer.json +++ b/curriculum/structure/superblocks/full-stack-developer.json @@ -718,6 +718,7 @@ "blocks": [ "lecture-classes-and-objects", "workshop-musical-instrument-inventory", + "workshop-email-simulator", "lab-budget-app", "review-classes-and-objects", "quiz-classes-and-objects"