WEBVTT

0
00:00.630 --> 00:04.770
Now I hope you've given this project a good go because I'm going to now go

1
00:04.770 --> 00:08.910
through the solution for the normal starting project. Now,

2
00:08.940 --> 00:13.770
the important thing to stress here is that if you used one of the hard starting 

3
00:13.770 --> 00:16.140
project or the extra hard starting project,

4
00:16.500 --> 00:20.850
you might have ended up with a different method of doing the same thing.

5
00:21.450 --> 00:24.360
And often I get students asking me,

6
00:24.360 --> 00:26.520
what is the right way of doing things?

7
00:26.820 --> 00:31.050
And this is not really the thing that you should focus on. Instead,

8
00:31.160 --> 00:33.950
<v 1>use your creativity, you use your skills</v>

9
00:34.430 --> 00:38.090
<v 0>and make sure that your program does what you want it to.</v>

10
00:38.510 --> 00:40.760
There is no real right answer.

11
00:40.880 --> 00:45.880
It really depends on your preferences or even what it is that you know how to

12
00:45.890 --> 00:46.670
do.

13
00:46.670 --> 00:51.500
If you can get this project to work using just your own skills and a bit of

14
00:51.500 --> 00:55.550
Googling, a bit of Stack Overflow, as long as you managed to get it to work,

15
00:55.580 --> 01:00.200
it's way more important than what is the right way of doing things.

16
01:00.680 --> 01:04.130
So all of that, I'm trying to say that I'm going to go through the solution,

17
01:04.370 --> 01:08.870
but don't view this as the ultimate solution. And if you did it a different way,

18
01:08.900 --> 01:13.850
that's totally fine as long as it works. Alright, let's get started. So here,

19
01:13.850 --> 01:17.420
I've got these starting files for the normal starting project,

20
01:17.960 --> 01:22.340
and I'm going to go through each of the lines line by line to create our final

21
01:22.400 --> 01:23.750
birthday wisher project.

22
01:24.320 --> 01:28.610
The first thing the instruction tells us to do is to go into this birthdays

23
01:28.610 --> 01:33.610
.csv and update this with some birthdays of our friends and family.

24
01:34.670 --> 01:37.100
So I'm going to replace all the data that's currently in here,

25
01:37.550 --> 01:41.660
and I'm going to add some data of my own. Now,

26
01:41.720 --> 01:45.980
the thing that we have to do in order to test this code is to make sure that one

27
01:45.980 --> 01:50.390
of these rows of data actually matches an email that we can access

28
01:50.780 --> 01:53.330
and also today's month and day.

29
01:53.840 --> 01:56.090
So today is July 14th

30
01:56.120 --> 02:00.140
so I'm going to update those two numbers and the year doesn't matter so much in

31
02:00.140 --> 02:02.480
this case. Once I've done that,

32
02:02.480 --> 02:07.340
I'm going to hit save, command + s or just go to file and go to save all.

33
02:08.270 --> 02:13.270
And you can also see the shortcut for your particular operating system there as

34
02:13.310 --> 02:16.490
well. Now, inside my main.py,

35
02:16.490 --> 02:20.300
I've now completed this step. So I'm going to go ahead and delete it.

36
02:22.220 --> 02:24.080
Now, step 2 is the hardest.

37
02:24.410 --> 02:29.410
And here we have to check if today's date matches a birthday in the birthday

38
02:30.170 --> 02:31.003
CSV.

39
02:31.400 --> 02:35.540
The first thing I'm going to do is I'm going to import datetime

40
02:35.990 --> 02:39.830
because that's going to be crucial for me to be able to figure out what today's

41
02:39.830 --> 02:43.880
date actually is. And in fact, that is what hint one says.

42
02:44.300 --> 02:48.530
It says to create a tuple from today's month and day using the datetime

43
02:48.530 --> 02:51.200
module. This is essentially what we want to create.

44
02:51.440 --> 02:53.840
So how do we get hold of today's month? Well,

45
02:53.870 --> 02:57.650
we can use the datetime module and then the datetime class,

46
02:57.710 --> 03:02.710
and then we can call now and we can get hold of the month like this.

47
03:04.870 --> 03:07.230
Now we can simplify this in a number of ways.

48
03:07.250 --> 03:11.680
We can create a alias for datetime as dt.

49
03:12.040 --> 03:15.610
So then we can shorten this by a little bit, or

50
03:15.610 --> 03:20.610
you can shorten it even more just by saying, well, from the datetime module

51
03:20.650 --> 03:24.220
let's go ahead and import just the datetime class.

52
03:24.790 --> 03:29.790
That way you can actually set this to simply as datetime.now.month.

53
03:31.450 --> 03:36.250
Then we have to get hold of today's day so we could copy this

54
03:36.310 --> 03:38.920
and instead of getting the month we could get the day.

55
03:39.640 --> 03:44.620
So now we've created today. Now you can of course split this into two parts

56
03:44.620 --> 03:46.510
if you find it easier to understand.

57
03:46.840 --> 03:51.010
You could say that today well is equal to datetime.now,

58
03:51.670 --> 03:55.000
and then today_tuple is equal to a tuple

59
03:55.030 --> 04:00.030
which is today.month, today.day.

60
04:00.760 --> 04:04.600
So this is another variation and there's millions of variations out there

61
04:04.600 --> 04:08.860
of course. So I think we're now done with hint 1 so let's go and delete it.

62
04:09.340 --> 04:10.870
Now we're onto hint 2.

63
04:11.050 --> 04:14.260
We're going to use pandas to read from the birthday CSV.

64
04:14.620 --> 04:19.480
So we're going to import pandas. And once we've imported that,

65
04:19.510 --> 04:24.510
we're going to use pandas to read CSV and then provide our file name

66
04:25.030 --> 04:27.490
which is birthdays.csv.

67
04:28.000 --> 04:31.720
So I'm going to save this as a variable called data. And remember,

68
04:31.750 --> 04:33.910
this is now a pandas dataframe

69
04:34.420 --> 04:39.280
which we can use to do a number of things with including using these

70
04:39.280 --> 04:42.610
instructions to create a birthday dictionary.

71
04:43.330 --> 04:47.530
So what we're going to do is we're going to create a dictionary comprehension,

72
04:48.100 --> 04:52.960
and I provided the template here for you already. So you can use that

73
04:53.050 --> 04:58.050
iterrows in order to iterate through our dataframe that was created here,

74
04:58.480 --> 05:00.100
so data.iterrows,

75
05:00.310 --> 05:05.260
and then we'll get hold of the index of each of those rows and also the data in

76
05:05.260 --> 05:06.160
each of the rows.

77
05:06.760 --> 05:11.760
And then we could create a new dictionary with a new key and a new value from

78
05:12.040 --> 05:14.470
all of these things that we've iterated through.

79
05:15.130 --> 05:19.450
What is that new key going to be? Well, it's going to look something like this,

80
05:19.480 --> 05:20.313
right?

81
05:20.590 --> 05:25.570
We're hoping to create this birthday dictionary using this particular format.

82
05:26.140 --> 05:31.140
Now the key is going to be a tuple that consists of the birthday month and the

83
05:31.600 --> 05:32.433
birthday day

84
05:32.830 --> 05:37.830
so let's replace that new key with a tuple. And the birthday month is of course

85
05:38.050 --> 05:41.710
going to come from our data row. So that we're going to say data_row

86
05:42.040 --> 05:43.930
and then we can either use square brackets

87
05:43.960 --> 05:48.960
or we can use the dot notation to get hold of the data in this month column.

88
05:50.440 --> 05:55.440
So you can either say data_row.month or data_row square brackets

89
05:56.050 --> 05:59.540
and then month, like this. So it's totally up to you.

90
06:00.200 --> 06:04.790
And then the next item in the tuple which is still in the key,

91
06:04.790 --> 06:09.080
so we're still before the colon here, is going to be the birthday_day.

92
06:09.110 --> 06:10.970
So that's going to be the data_row

93
06:11.330 --> 06:14.540
and then we have to pass in the name of the day column,

94
06:14.570 --> 06:16.670
which is just the word day.

95
06:19.370 --> 06:20.600
Now, once we've done that,

96
06:20.720 --> 06:25.720
then we have to provide what is the value for each of these key value pairs in

97
06:26.000 --> 06:29.690
our dictionary. Well, what we actually want is the data row,

98
06:29.690 --> 06:34.400
so the entire row of data, and we want it to look a bit like this.

99
06:34.430 --> 06:37.730
So we have our tuple with our month and our day as the key

100
06:38.060 --> 06:40.820
and then the value is just the entire row of data.

101
06:41.270 --> 06:46.070
So we can simply replace this value with our data row like this.

102
06:46.730 --> 06:51.730
Now we're pretty much done with this part and we can delete all of these bits of

103
06:51.950 --> 06:56.420
hints and comments, and I can delete this example code as well.

104
06:57.080 --> 06:59.300
Now that we've got our birthday dictionary,

105
06:59.390 --> 07:04.390
the next thing we want to do is we want to check to see if today matches a

106
07:04.490 --> 07:06.170
birthday in the birthdays.csv.

107
07:06.470 --> 07:11.120
So you wanna check if this tuple exists inside this birthday_dict

108
07:11.510 --> 07:15.830
and if it matches one of the actual birthday month and days.

109
07:16.310 --> 07:20.720
So we can do something like this; if today_month, today_

110
07:20.720 --> 07:23.660
day is in our birthdays dictionary.

111
07:24.200 --> 07:28.580
Now this today_month today_day is of course going to be our today_

112
07:28.670 --> 07:31.940
tuple. And this way with this

113
07:31.970 --> 07:36.970
if statement we can check to see if this today_tuple with the month and day

114
07:37.400 --> 07:42.200
actually exists as one of our friends and family's birthdays inside this

115
07:42.200 --> 07:45.890
birthdays_dict. And once we've done that,

116
07:46.010 --> 07:51.010
then we can get rid of step 2 and move on to step 3. In step 3

117
07:51.290 --> 07:55.130
we're saying, well, if there is a match, so if this if statement is true,

118
07:55.580 --> 07:58.970
then we're gonna pick a random letter out of letter_1, 2,

119
07:58.970 --> 08:03.970
3, from our letter templates inside here. To do that,

120
08:04.070 --> 08:06.830
we're going to need to think about the relative file path.

121
08:06.830 --> 08:10.850
So we have to access the letter template folder to get to

122
08:10.850 --> 08:11.960
each of these files.

123
08:12.440 --> 08:17.440
The file path will look something like this.

124
08:17.870 --> 08:21.740
It will be letter_template/ one of these letters for example,

125
08:21.740 --> 08:25.910
letter_1.txt or letter_2 or 3. In fact,

126
08:25.940 --> 08:30.380
this particular number is going to be something that we're going to generate

127
08:30.440 --> 08:33.560
randomly and we're going to put in there as a f-string.

128
08:34.160 --> 08:38.780
So I need to now import the random module in addition to everything else.

129
08:39.320 --> 08:44.320
And we can go ahead and call the random module and get a randint between one and

130
08:46.490 --> 08:47.323
three.

131
08:47.410 --> 08:48.820
Like this.

132
08:50.140 --> 08:53.320
So now we've completed these two hints, and finally,

133
08:53.350 --> 08:58.350
we're going to use the replace() method to replace the name placeholder inside each of

134
08:58.740 --> 09:03.740
these letters right here with the actual name of the person whose birthday it is.

135
09:04.830 --> 09:09.030
Here I've linked it to the documentation for the replace keyword

136
09:09.960 --> 09:14.550
and this is what it looks like. For example, if we have a string like this,

137
09:14.580 --> 09:18.930
then we can say text.replace, take the word that we want to take out

138
09:18.990 --> 09:22.140
and then the word we want to put in. In our case,

139
09:22.290 --> 09:26.730
we're going to have to read the text from this file with our 

140
09:26.730 --> 09:31.110
with open, and then the file path goes in here,

141
09:31.440 --> 09:34.290
and then we'll call this the letter_file.

142
09:35.700 --> 09:37.200
And then from this letter_file,

143
09:37.200 --> 09:42.000
we're going to get the contents by reading from this letter_file.

144
09:42.380 --> 09:43.213
<v 3>Right.</v>

145
09:44.990 --> 09:49.340
<v 0>Now, once we've gotten the contents, then we can say contents.replace</v>

146
09:49.730 --> 09:54.500
and the thing that we want to replace is that hard-coded name placeholder

147
09:54.980 --> 09:59.750
and the thing we want to replace it with comes from the current matching

148
09:59.780 --> 10:00.613
birthday.

149
10:02.120 --> 10:06.800
If the today_tuple exists inside our birthday dictionary,

150
10:07.250 --> 10:10.100
then we can get hold of the entire row of data

151
10:10.550 --> 10:11.383
<v 3>...</v>

152
10:14.660 --> 10:19.660
<v 0>by taking our birthday dictionary and then getting hold of the item at the key</v>

153
10:21.380 --> 10:23.240
which matches today_tuple.

154
10:24.650 --> 10:29.240
Remember that today_tuple is the month and day as a tuple and the birthday

155
10:29.240 --> 10:34.240
dictionaries all have keys as the birthday month and birthday day as a tuple.

156
10:35.030 --> 10:39.260
So we've checked that this tuple exists as a key inside the birthday

157
10:39.260 --> 10:40.093
dictionary,

158
10:40.250 --> 10:45.200
and then we use the square bracket to actually access the data row for that

159
10:45.200 --> 10:49.130
particular match. And then we've got the birthday_person,

160
10:49.490 --> 10:51.740
so from that birthday_person

161
10:52.100 --> 10:54.740
we're going to get hold of their name.

162
10:55.190 --> 10:58.940
And the name lives under the name column here.

163
10:59.570 --> 11:01.610
As long as we put the name as a string,

164
11:01.970 --> 11:05.510
then we should be able to get hold of their actual name right here.

165
11:06.770 --> 11:09.920
And that is the end of step 3.

166
11:10.550 --> 11:13.130
So now we're finally moving on to step 4

167
11:13.370 --> 11:15.380
where we're going to send our letter

168
11:15.380 --> 11:18.650
which was generated here in our contents,

169
11:19.010 --> 11:22.400
and we're going to send it to that birthday person's email address.

170
11:23.840 --> 11:27.470
So the first thing we're going to need is to create a connection and we're going

171
11:27.470 --> 11:30.770
to use the smtplib library to do that.

172
11:31.130 --> 11:35.750
So import smtplib. And then we're going to say with smtp

173
11:35.750 --> 11:39.830
lib.SMTP, let's create an object from that

174
11:40.130 --> 11:44.480
and we have to provide the host string. So as I mentioned before,

175
11:44.480 --> 11:48.350
depending on which email provider you use, it will be different.

176
11:48.860 --> 11:53.270
Now I'm going to put in my email and password at the top as constants.

177
11:53.770 --> 11:55.420
Of course, this is not going to work for you

178
11:55.420 --> 11:58.450
so you're going to have to use your own email and password.

179
11:59.020 --> 12:01.660
And depending on which email provider you use,

180
12:01.690 --> 12:06.010
you'll have to pick the correct SMTP. So I'm sending emails

181
12:06.010 --> 12:10.120
using Gmail so I'm going to change this to smtp.gmail.com.

182
12:10.690 --> 12:15.690
And I'm going to save this connection that I create as the connection.

183
12:16.630 --> 12:21.630
Step 2 is to go ahead and call the starrttls.

184
12:23.050 --> 12:27.190
So we'll get our connection and then call starttls.

185
12:27.670 --> 12:32.670
And then step 3  is to log in to our connection by calling login.

186
12:35.530 --> 12:40.150
And then we have to provide the email that we want to log into and the password

187
12:40.180 --> 12:44.920
for that email account. Finally,

188
12:44.980 --> 12:49.980
we're going to set up the subject and the content by calling connection.

189
12:50.350 --> 12:55.060
sendmail. And the from address is MY_EMAIL,

190
12:55.630 --> 13:00.280
the to address is going to be the email of the birthday person.

191
13:00.730 --> 13:03.130
So we're going to get our birthday_person

192
13:03.820 --> 13:08.710
and then we're going to get hold of their email by looking at the name of the

193
13:08.740 --> 13:12.460
email column, which is just email actually like this.

194
13:12.970 --> 13:16.870
And then finally, we can add in the actual message.

195
13:18.880 --> 13:19.690
I'm going to again,

196
13:19.690 --> 13:23.230
format this so that it's a little bit easier to read for you.

197
13:25.480 --> 13:27.550
And then we're going to set up our message.

198
13:27.760 --> 13:32.760
This is going to be an f-string and the subject is always going to be the same.

199
13:33.130 --> 13:35.110
It's going to be happy birthday.

200
13:36.280 --> 13:40.840
And then after two of these new lines, we can add the actual message body.

201
13:41.350 --> 13:45.970
Now the message body is going to be the contents that we created because, 

202
13:45.970 --> 13:49.360
remember, that takes a random letter, reads it

203
13:49.420 --> 13:53.500
and then replaces in the name placeholder with the birthday person's name.

204
13:54.130 --> 13:55.870
So that's what we're going to put in here.

205
13:56.440 --> 13:59.860
And now that's step 4 completed as well

206
14:00.220 --> 14:04.150
and we can now go ahead and run this file.

207
14:04.630 --> 14:09.250
And hopefully it will show us process finished with exit code zero,

208
14:09.280 --> 14:13.030
which is the best thing we can see. That means everything went well.

209
14:13.510 --> 14:15.610
And remember that the birthday

210
14:15.610 --> 14:19.840
which I set to test was this row of data.

211
14:20.320 --> 14:25.320
So it should have replaced a random letter's placeholder with the name of mum

212
14:26.650 --> 14:31.540
and it should've sent that email to appbreweryinfo@gmail.com. So now

213
14:31.540 --> 14:34.870
if I take a look at my appbreweryinfo@gmail.com,

214
14:35.380 --> 14:39.280
you can see the email come through, but you might notice a bug here.

215
14:39.700 --> 14:42.820
The name was actually never replaced. Instead,

216
14:42.880 --> 14:44.710
we're still seeing this placeholder.

217
14:45.100 --> 14:48.550
So this is a really common mistake that a lot of people make

218
14:48.850 --> 14:53.850
and the reason is because the text replacement comes out as the output.

219
14:54.620 --> 14:58.880
So it doesn't actually change the original text just by calling that method.

220
14:59.240 --> 14:59.810
Instead,

221
14:59.810 --> 15:04.760
you have to save it to a new variable in order to see the replaced text.

222
15:05.720 --> 15:10.250
So in our case what that means is we can't just say content.replace.

223
15:10.520 --> 15:14.660
We actually have to say contents = contents.replace.

224
15:14.690 --> 15:19.690
So we replace the contents and then we save it back to the original variable.

225
15:21.260 --> 15:25.460
So now if I run this again, you can see that this time

226
15:25.490 --> 15:28.940
our message actually has the name replaced in here,

227
15:29.270 --> 15:34.270
it has a random letter that it's generated and it has sent it to the correct

228
15:34.490 --> 15:39.140
email address. If you want to take a look at the completed code,

229
15:39.200 --> 15:44.180
then you'll find it in the course resources as the end code for the birthday

230
15:44.180 --> 15:45.140
wisher project

231
15:45.530 --> 15:49.220
and this contains a lot of things that we've talked about so far,

232
15:49.520 --> 15:53.180
including dictionary comprehension, reading CSVs,

233
15:53.210 --> 15:57.590
creating dataframes from pandas, using and creating tuples,

234
15:57.980 --> 16:02.780
working with dictionaries, file paths, opening files,

235
16:02.840 --> 16:05.870
replacing files, reading them, and of course,

236
16:05.930 --> 16:09.920
writing and sending email as well as the datetime module.

237
16:10.550 --> 16:13.370
So if you got stuck on any of these aspects,

238
16:13.760 --> 16:18.590
then be sure to take a look at the previous lesson where we covered them

239
16:18.650 --> 16:23.650
because we covered all of these concepts in a lot of detail in previous lessons.

240
16:24.530 --> 16:28.070
So make sure that you don't just skip ahead if you actually got stuck.

241
16:28.160 --> 16:33.160
It probably is an indicator that it's a good time to revisit and review some of

242
16:33.800 --> 16:37.910
these concepts. For example, if you got stuck on dictionary comprehension,

243
16:38.180 --> 16:42.140
then go back to the lessons where we did list and dictionary comprehensions

244
16:42.440 --> 16:46.490
and try out some of the exercises again, just to refresh your mind. Now,

245
16:46.520 --> 16:51.520
if you got stuck on working with the iterrows or the data that comes from our

246
16:52.520 --> 16:57.020
pandas dataframes, then go back to those lessons where we talked about CSVs,

247
16:57.020 --> 17:00.770
and we talked about pandas because nothing here is new,

248
17:01.070 --> 17:06.070
but it does require your understanding and your ability to know how to apply the

249
17:06.890 --> 17:10.550
knowledge that you've learned before. Now,

250
17:10.640 --> 17:15.640
the final thing I want to show you in the next lesson is how to get this code to

251
17:16.310 --> 17:21.310
actually run every day so it can actually check every single day to see if it

252
17:22.190 --> 17:24.500
needs to send out a birthday email.

253
17:24.980 --> 17:27.650
So for all of that and more, I'll see you on the next lesson.