In the previous lessons today,
we looked at how we can use the JSON format to store and load data into our
program.
We also looked at how we can use the try, except, else, and finally keywords to
handle exceptions in our program to make sure that it doesn't fail and end up
crashing our program so the user ends up with a bad user experience.
Instead, we can anticipate the things that might happen. For example,
the first time when we run our program,
we might not have a data.json file and we deal with it accordingly.
Now, all that's left to do is to put in the last bit of functionality,
which is the search functionality, because after all,
we don't want to be digging through a JSON file to find out all email and
password combinations for the websites that we've stored.
So what we want to be able to do is to add a search button
and it's going to look very similar in size to the generate password button
and it's going to be in the same grid column. Now,
when we type in a website and we hit search,
it's going to look through our JSON, find that data,
load that data up and look through it for a key that matches the one the user
typed in. If they find it,
then we get a pop up showing up and it tells us the email and the password
that's associated with that account.
So you've actually done all of this before. Creating tkinter widgets,
laying out tkinter widgets,
creating message boxes or these popups and giving it a title,
giving it a message.
You've also seen how you can load JSON data and get the data in the format of a
Python dictionary. Now, once you have the Python dictionary,
then you can use your usual dictionary methods to try and get hold of the data
that's inside. And finally,
you also know how to deal with exceptions.
So remember, there might also be an exception in this case,
because if this was the first time that we were running this password manager,
and in fact,
we hadn't stored any websites or any emails or passwords in our database,
then if you hit search, it might actually just give you a file
not found error as well. So there's quite a few components involved,
but I'm pretty sure that you can actually get this to work.
So the end outcome you're looking for looks something like this in terms of
layout,
and you should be able to test this so that when you start out without a data
.json file and you hit search, it should tell you error,
No data file found.
But, if you had saved a password under that name and you added it to the
database, then you try to find it,
then it should find you the email and the password that you saved.
So this is what you're aiming for
and I'm pretty sure that you can do it with just a little bit of thought and
a little bit of time spent problem-solving. Once you're ready,
pause the video and give this final challenge a go to complete the password
manager
project. Good luck.
Alright, so I hope
you gave that a good go. And as I always say, if there's no struggle,
there's no learning.
So make sure that you've allocated at least 20 minutes to trying things out and
figuring things out and debugging and just messing with the code yourself before
you're coming to the solution. But all right,
I'm going to walk through the solution with you together.
And the first thing we're going to do is we're going to add a button next to our
website entry.
So right here. Now notice how we've got our three columns in our grid system.
So our search button is going to go in pretty much in the same column as our
generate password button. So let's go ahead and create our
search button.
This is going to be a button with the text that says
Search.
Now, once we've created that, let's go ahead and lay it out
uusing the grid system.
Now the search button is going to go onto the grid in the same row as the
website label and the website entry. So that is row 1.
So let's go ahead and add row equals 1 and column-wise,
it's going to go in the same column as the generate password.
So that is a column 2. Now, if we run our code, as it is
however,
you'll see that the search button seems to be sitting inside the entry.
And the reason is because initially when we created this entry,
we gave it a column span of 2
so that it spans this second and third column.
So we have to change that.
Let's go and find our website entry and delete this column span equals 2.
So now you can see that this has a column span of 1,
so it's only in this middle column,
but it's so large that it's pushing the rest of our layout out of the way.
So what we need to do is we need to make this entry
the same width as the password entry
so that it'll actually fit into our layout.
So our password entry has a width of 21.
So our website entry is also going to need to change down to a smaller size.
Now you can see the entry is now the same size and its looking a lot better. If
you want, and this is really not strictly necessary
but I think it's quite neat, is you can tweak and adjust the width of this search
button so that it's roughly the same as the generate password button.
Now this button is this width because it's what is needed to accommodate all of
the words in that button, but we can manually change the search button
so that it's about the same width.
And in this case, I'm literally just using trial and error to figure it out. Width as
10 looks maybe a little bit small, let's try width of 13
and it looks just about right. Now that I'm done with my layout,
it's onto the next part which is adding functionality.
Now the functionality is going to come when the search button is pressed.
So I'm going to add a command to this
and the command is going to call a method which I'll call find_password. We've
got generate_password and we'll now have find_password.
Now I'm going to go and create my find_password.
And just because we've got all these sections set up, I'm going to add
another section which I'll call find_password. Inside
the section is where we're going to be creating our
find_ password function. And this find_password,
let's just check to make sure there were no errors here
and we've spelled at the same way.
And this find_password function is going to need to get hold of the entry
that's inside the website entry.
So the website that we're looking for comes from the website_entry.get,
and then we'll be able to get the value of whatever the user typed inside this
entry.
And then we're going to use that to search through our data.json.
Now the easiest way for this to work is actually to first add a website.
So I'm just going to generate a website with my email and password and add it so
that it gets saved inside my data.json.
This way I'll be able to print things and I'll be able to work with the data a
little bit easier.
And then we'll address the exception catching a little bit later on.
What exactly are we looking for? Well,
we're looking to load up the data inside this JSON file.
So we're going to be opening up that file.
So with open and then the filename is data.json,
and then we'll save this as the data_file.
Now, once we've got our data file,
then we can use our JSON module to load up that file
and we're going to be getting hold of the data that's inside.
So we can save this to a variable called data.
Now this data, if I go ahead and print it,
you should remember is basically just a dictionary.
So as soon as I hit search, that should trigger this find_password
and now I've got my dictionary printed in here.
So what do we want to do with this dictionary? Well,
we want to look through it and check to see if this website that we're searching
for actually exists inside that dictionary.
The way we do that is we say if website in data,
well, in that case, it's actually success, right?
We've managed to find it inside the data
and we now just need to get hold of the email and password values.
So let's create a variable called email and we want to pick out this particular
value from this dictionary.
If this is the entire dictionary,
then we want to get hold of the dictionary and pick out the item with the key
that matches the website that we're searching for, like this.
Now, once we've gotten hold of this,
this is going to be a nested dictionary.
So it's equivalent to basically this part of the dictionary.
So this in itself is the dictionary,
which you could save to a separate variable,
but I think it's actually easy enough to understand that we can tag on another
key here
which is the word email in order to get hold of the value that's associated with
that key. So that way,
this email variable now saves this value here.
And in the same way, we can get the password from data
getting hold of the data under the website key and then passing in the password
key. Now that we've got the email and password,
we can go ahead and create our message box using show info. And we'll set the
title to be the website that the user is searching for
and the message is going to be a f-string,
which has the email listed.
And then on a new line,
it's also got the password that's going to be listed as well.
Like that.
Let's go ahead and run this code and make sure that it works.
Let's say that we have a couple more passwords that we save in here.
Now I should have three entries, Amazon, eBay, and Twitter,
and I want to go and search for my password for eBay.
Once I hit the search button, it goes into that find_password function
and if it finds that one of these has the key eBay,
then it's going to fetch all of these relevant pieces of data that I wanted from
that JSON file.
So now what we have to do is to make sure that we catch any exceptions that
might occur and you might already have realized that this works really well
right now because we have data
and because we have this data.json file.
But the first time that I run my program,
this is not going to exist.
So this is a more realistic version of what the program will look like
the first time we run it. And the first time that we go ahead and run this
and I search for something that is going to look for a file that doesn't exist
immediately, our program crashes, and we get the file not found error.
So what we need to do is we need to be aware enough that these things like
opening files can fail.
So what we have to do is we have to catch the exceptions and handle them.
Let's go ahead and indent this entire block and put it inside the try.
Now, the part that's most likely to fail in fact,
the first time the program starts, this is definitely going to fail.
But what we want to do is we want to catch the exception
that is file not found error. And in the situation,
what we want to do is we want to create a message box that tells the user
this data file doesn't exist.
This is just a simple showinfo
and we'll give it a title of error and we'll give it a message of no data file
found,
or you can put any other message you can think of. However,
if we were successful in opening this file,
then this is the part of the code that we want to implement.
So let's go ahead and bring it up to the right indentation.
And we searched through our data,
look for this website and then give the user a popup if it exists.
Now, if we run our code again and we search for something
even though we don't have a data file, we haven't saved anything yet,
then we get this error popup and it tells us no data files found.
And hopefully the user would realize, well,
actually I haven't actually saved any passwords.
So there's one other situation that we haven't yet addressed however.
If I had, um, let's say Amazon, um,
eBay and Twitter saved in my JSON file,
but I decided to search for something that I never saved.
Let's say my details for Facebook. Well in this situation, actually
nothing really happens. And it's a little bit confusing because for the user,
it seems like our program just doesn't work. But in fact, we
as the programmers know what the problem is. It's basically this
if statement is coming out as false. The website doesn't exist in the data.
So none of this gets carried out and the function ends.
That's probably not the best user experience. Instead,
we probably want to catch it with an else statement and we want to use a message
box to tell the that again,
there is a error in this situation
but the reason for this error is because
there are no details that currently exist for the website that they're searching
for. That way
they're not sitting there confused as to why searching for some sort of website
doesn't actually come up with anything.
It tells them that there are no details and they would have to add it
if they wanna find it in the database.
A lot of students might be wondering at this point, well,
why don't we catch this with some sort of exception?
And in fact, we could create an exception.
We could raise an exception here and then we could catch it somewhere else and
show this message box.
But the thing you want to keep in mind is that if you can do something with if
and else very easily, then you should stick to if and else.
If you can't do it with if and else very easily, and it's actually an error
that's going to be thrown that you don't have any other way of dealing with,
then you should be using the try, except, else, finally, keywords.
And in fact, if you think about this,
I don't have an easy way of checking to see if this data file ends up being not
found. But I do have this file not found error
which is going to be thrown every time that I try to open a file and that
doesn't exist.
The other way to think about it is that an exception is something that is
meant to be exceptional. It's something that happens very rarely.
Whereas if and else catches things that happen frequently.
It's pretty often that your user might search through the website
thinking that they saved some sort of details for a website. But in fact,
they didn't. Think about this when you're writing your code
and remember that only use exception handling when you don't have an easy
alternative. There you have it.
That's the completed code for our upgraded password manager.
If you want to take a look at any of this code,
then, as always, head over to the course resources
and you'll see a link to the completed code that you can review.
But in today's lessons,
we added a lot more knowledge regarding things like error handling and saving
data in JSON formats and handling and loading and updating and dealing with
all of that data in order to create this fully-fledged password manager project.
Now, I'm sure there's a lot more improvements you can add to this,
but it's going to go beyond the allocated time for today's lessons.
So if you want,
please continue messing around with the code and improving it and make your own
customizations and modifications.
If you create something that you thought was cool,
then be sure to share it with us in the Q/A below the lesson so that we can
all admire your hard work and maybe improve our own code as a result.
So have fun playing around with this program and I'll see you tomorrow.