Now I hope you've given this project a good go because I'm going to now go
through the solution for the normal starting project. Now,
the important thing to stress here is that if you used one of the hard starting
project or the extra hard starting project,
you might have ended up with a different method of doing the same thing.
And often I get students asking me,
what is the right way of doing things?
And this is not really the thing that you should focus on. Instead,
1: use your creativity, you use your skills
0: and make sure that your program does what you want it to.
There is no real right answer.
It really depends on your preferences or even what it is that you know how to
do.
If you can get this project to work using just your own skills and a bit of
Googling, a bit of Stack Overflow, as long as you managed to get it to work,
it's way more important than what is the right way of doing things.
So all of that, I'm trying to say that I'm going to go through the solution,
but don't view this as the ultimate solution. And if you did it a different way,
that's totally fine as long as it works. Alright, let's get started. So here,
I've got these starting files for the normal starting project,
and I'm going to go through each of the lines line by line to create our final
birthday wisher project.
The first thing the instruction tells us to do is to go into this birthdays
.csv and update this with some birthdays of our friends and family.
So I'm going to replace all the data that's currently in here,
and I'm going to add some data of my own. Now,
the thing that we have to do in order to test this code is to make sure that one
of these rows of data actually matches an email that we can access
and also today's month and day.
So today is July 14th
so I'm going to update those two numbers and the year doesn't matter so much in
this case. Once I've done that,
I'm going to hit save, command + s or just go to file and go to save all.
And you can also see the shortcut for your particular operating system there as
well. Now, inside my main.py,
I've now completed this step. So I'm going to go ahead and delete it.
Now, step 2 is the hardest.
And here we have to check if today's date matches a birthday in the birthday
CSV.
The first thing I'm going to do is I'm going to import datetime
because that's going to be crucial for me to be able to figure out what today's
date actually is. And in fact, that is what hint one says.
It says to create a tuple from today's month and day using the datetime
module. This is essentially what we want to create.
So how do we get hold of today's month? Well,
we can use the datetime module and then the datetime class,
and then we can call now and we can get hold of the month like this.
Now we can simplify this in a number of ways.
We can create a alias for datetime as dt.
So then we can shorten this by a little bit, or
you can shorten it even more just by saying, well, from the datetime module
let's go ahead and import just the datetime class.
That way you can actually set this to simply as datetime.now.month.
Then we have to get hold of today's day so we could copy this
and instead of getting the month we could get the day.
So now we've created today. Now you can of course split this into two parts
if you find it easier to understand.
You could say that today well is equal to datetime.now,
and then today_tuple is equal to a tuple
which is today.month, today.day.
So this is another variation and there's millions of variations out there
of course. So I think we're now done with hint 1 so let's go and delete it.
Now we're onto hint 2.
We're going to use pandas to read from the birthday CSV.
So we're going to import pandas. And once we've imported that,
we're going to use pandas to read CSV and then provide our file name
which is birthdays.csv.
So I'm going to save this as a variable called data. And remember,
this is now a pandas dataframe
which we can use to do a number of things with including using these
instructions to create a birthday dictionary.
So what we're going to do is we're going to create a dictionary comprehension,
and I provided the template here for you already. So you can use that
iterrows in order to iterate through our dataframe that was created here,
so data.iterrows,
and then we'll get hold of the index of each of those rows and also the data in
each of the rows.
And then we could create a new dictionary with a new key and a new value from
all of these things that we've iterated through.
What is that new key going to be? Well, it's going to look something like this,
right?
We're hoping to create this birthday dictionary using this particular format.
Now the key is going to be a tuple that consists of the birthday month and the
birthday day
so let's replace that new key with a tuple. And the birthday month is of course
going to come from our data row. So that we're going to say data_row
and then we can either use square brackets
or we can use the dot notation to get hold of the data in this month column.
So you can either say data_row.month or data_row square brackets
and then month, like this. So it's totally up to you.
And then the next item in the tuple which is still in the key,
so we're still before the colon here, is going to be the birthday_day.
So that's going to be the data_row
and then we have to pass in the name of the day column,
which is just the word day.
Now, once we've done that,
then we have to provide what is the value for each of these key value pairs in
our dictionary. Well, what we actually want is the data row,
so the entire row of data, and we want it to look a bit like this.
So we have our tuple with our month and our day as the key
and then the value is just the entire row of data.
So we can simply replace this value with our data row like this.
Now we're pretty much done with this part and we can delete all of these bits of
hints and comments, and I can delete this example code as well.
Now that we've got our birthday dictionary,
the next thing we want to do is we want to check to see if today matches a
birthday in the birthdays.csv.
So you wanna check if this tuple exists inside this birthday_dict
and if it matches one of the actual birthday month and days.
So we can do something like this; if today_month, today_
day is in our birthdays dictionary.
Now this today_month today_day is of course going to be our today_
tuple. And this way with this
if statement we can check to see if this today_tuple with the month and day
actually exists as one of our friends and family's birthdays inside this
birthdays_dict. And once we've done that,
then we can get rid of step 2 and move on to step 3. In step 3
we're saying, well, if there is a match, so if this if statement is true,
then we're gonna pick a random letter out of letter_1, 2,
3, from our letter templates inside here. To do that,
we're going to need to think about the relative file path.
So we have to access the letter template folder to get to
each of these files.
The file path will look something like this.
It will be letter_template/ one of these letters for example,
letter_1.txt or letter_2 or 3. In fact,
this particular number is going to be something that we're going to generate
randomly and we're going to put in there as a f-string.
So I need to now import the random module in addition to everything else.
And we can go ahead and call the random module and get a randint between one and
three.
Like this.
So now we've completed these two hints, and finally,
we're going to use the replace() method to replace the name placeholder inside each of
these letters right here with the actual name of the person whose birthday it is.
Here I've linked it to the documentation for the replace keyword
and this is what it looks like. For example, if we have a string like this,
then we can say text.replace, take the word that we want to take out
and then the word we want to put in. In our case,
we're going to have to read the text from this file with our
with open, and then the file path goes in here,
and then we'll call this the letter_file.
And then from this letter_file,
we're going to get the contents by reading from this letter_file.
3: Right.
0: Now, once we've gotten the contents, then we can say contents.replace
and the thing that we want to replace is that hard-coded name placeholder
and the thing we want to replace it with comes from the current matching
birthday.
If the today_tuple exists inside our birthday dictionary,
then we can get hold of the entire row of data
3: ...
0: by taking our birthday dictionary and then getting hold of the item at the key
which matches today_tuple.
Remember that today_tuple is the month and day as a tuple and the birthday
dictionaries all have keys as the birthday month and birthday day as a tuple.
So we've checked that this tuple exists as a key inside the birthday
dictionary,
and then we use the square bracket to actually access the data row for that
particular match. And then we've got the birthday_person,
so from that birthday_person
we're going to get hold of their name.
And the name lives under the name column here.
As long as we put the name as a string,
then we should be able to get hold of their actual name right here.
And that is the end of step 3.
So now we're finally moving on to step 4
where we're going to send our letter
which was generated here in our contents,
and we're going to send it to that birthday person's email address.
So the first thing we're going to need is to create a connection and we're going
to use the smtplib library to do that.
So import smtplib. And then we're going to say with smtp
lib.SMTP, let's create an object from that
and we have to provide the host string. So as I mentioned before,
depending on which email provider you use, it will be different.
Now I'm going to put in my email and password at the top as constants.
Of course, this is not going to work for you
so you're going to have to use your own email and password.
And depending on which email provider you use,
you'll have to pick the correct SMTP. So I'm sending emails
using Gmail so I'm going to change this to smtp.gmail.com.
And I'm going to save this connection that I create as the connection.
Step 2 is to go ahead and call the starrttls.
So we'll get our connection and then call starttls.
And then step 3 is to log in to our connection by calling login.
And then we have to provide the email that we want to log into and the password
for that email account. Finally,
we're going to set up the subject and the content by calling connection.
sendmail. And the from address is MY_EMAIL,
the to address is going to be the email of the birthday person.
So we're going to get our birthday_person
and then we're going to get hold of their email by looking at the name of the
email column, which is just email actually like this.
And then finally, we can add in the actual message.
I'm going to again,
format this so that it's a little bit easier to read for you.
And then we're going to set up our message.
This is going to be an f-string and the subject is always going to be the same.
It's going to be happy birthday.
And then after two of these new lines, we can add the actual message body.
Now the message body is going to be the contents that we created because,
remember, that takes a random letter, reads it
and then replaces in the name placeholder with the birthday person's name.
So that's what we're going to put in here.
And now that's step 4 completed as well
and we can now go ahead and run this file.
And hopefully it will show us process finished with exit code zero,
which is the best thing we can see. That means everything went well.
And remember that the birthday
which I set to test was this row of data.
So it should have replaced a random letter's placeholder with the name of mum
and it should've sent that email to appbreweryinfo@gmail.com. So now
if I take a look at my appbreweryinfo@gmail.com,
you can see the email come through, but you might notice a bug here.
The name was actually never replaced. Instead,
we're still seeing this placeholder.
So this is a really common mistake that a lot of people make
and the reason is because the text replacement comes out as the output.
So it doesn't actually change the original text just by calling that method.
Instead,
you have to save it to a new variable in order to see the replaced text.
So in our case what that means is we can't just say content.replace.
We actually have to say contents = contents.replace.
So we replace the contents and then we save it back to the original variable.
So now if I run this again, you can see that this time
our message actually has the name replaced in here,
it has a random letter that it's generated and it has sent it to the correct
email address. If you want to take a look at the completed code,
then you'll find it in the course resources as the end code for the birthday
wisher project
and this contains a lot of things that we've talked about so far,
including dictionary comprehension, reading CSVs,
creating dataframes from pandas, using and creating tuples,
working with dictionaries, file paths, opening files,
replacing files, reading them, and of course,
writing and sending email as well as the datetime module.
So if you got stuck on any of these aspects,
then be sure to take a look at the previous lesson where we covered them
because we covered all of these concepts in a lot of detail in previous lessons.
So make sure that you don't just skip ahead if you actually got stuck.
It probably is an indicator that it's a good time to revisit and review some of
these concepts. For example, if you got stuck on dictionary comprehension,
then go back to the lessons where we did list and dictionary comprehensions
and try out some of the exercises again, just to refresh your mind. Now,
if you got stuck on working with the iterrows or the data that comes from our
pandas dataframes, then go back to those lessons where we talked about CSVs,
and we talked about pandas because nothing here is new,
but it does require your understanding and your ability to know how to apply the
knowledge that you've learned before. Now,
the final thing I want to show you in the next lesson is how to get this code to
actually run every day so it can actually check every single day to see if it
needs to send out a birthday email.
So for all of that and more, I'll see you on the next lesson.